在 Kotlin 中使用 `?` 进行类似 Rust 的错误处理,这可能吗?

Rust-like error handling in Kotlin using `?`, is it possible?

下面的代码有几个可能的失败。例如,width 可能为空,或者 r 可能为假。在所有情况下,我应该 return 一个 result.error() 或类似的东西。

  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    if (call.method == "getPlatformVersion") {
      result.success("Android ${android.os.Build.VERSION.RELEASE}")
    } else if (call.method=="registerTexture") {
      val entry: TextureRegistry.SurfaceTextureEntry = texture_registry.createSurfaceTexture();
      val surfaceTexture = entry.surfaceTexture();
      //TODO: return non-sucess when no width and height passed
      val width: Int = call.argument("width")!!
      val height: Int = call.argument("height")!!
      surfaceTexture.setDefaultBufferSize(width, height)
      val response = HashMap<String, Long>()
      RendererPlugin.surfaceTextureMap.put(entry, surfaceTexture)
      val r = RendererPlugin.registerSurfaceTextureNativeHandler(entry.id(), surfaceTexture)
      if (!r) {
        Log.d(LOG_TAG, "attention: failed result from registerSurfaceTextureNativeHandler")
      }
      response.put("textureId", entry.id())
      result.success(response)
    }
  }

在 Rust 上,我将所有这些都放入一个闭包中,结果为 Result<(), Error>,然后在 onMethodCall 中执行闭包,如果出现错误,我会 return 一个错误。此外,闭包将充满以 ? 结尾的调用,因此它会自动 return 将具有 From<> 实现的错误转换为 Error.

如何在 Kotlin 中高效地执行此操作?有没有办法做一个闭包,也很容易在这个闭包中 return 成功或错误,然后根据这个结果我调用 result.sucessresult.error?

希望对您有所帮助。

注意:我假设 result.success() 接受 Any 因为你 return StringHashMap 作为参数,但是 kotlin.Result没有几个通用的成功参数。您可以使用 kotlin.Pair<A, B> 作为 kotlin.Result 的成功类型:D

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    val finalResult: kotlin.Result<Any> = call.run {
        if (method == "getPlatformVersion") kotlin.Result.success("Android ${android.os.Build.VERSION.RELEASE}")
        else if (method == "registerTexture") {
            val entry: TextureRegistry.SurfaceTextureEntry = texture_registry.createSurfaceTexture();
            val surfaceTexture = entry.surfaceTexture();
            val width = argument("width") ?: return@run kotin.Result.failure(IllegalArgumentException("width is null"))
            val height = argument("height") ?: return@run kotlin.Result.failure(IllegalArgumentException("height is null"))

            surfaceTexture.setDefaultBufferSize(width, height)
            val response = HashMap<String, Long>()
            RendererPlugin.surfaceTextureMap.put(entry, surfaceTexture)
            val r = RendererPlugin.registerSurfaceTextureNativeHandler(entry.id(), surfaceTexture)
            if (!r) return@run kotlin.Result.failure(IllegalArgumentException("r is false"))
            response.put("textureId", entry.id())
            kotlin.Result.success(response)
        }
        else kotlin.Result.failure(IllegalStateException("undefined call.method"))
    }

    with (finalResult) {
        onSuccess(result::success)
        onFailure(result::fail)
    }
}

有一个 runCatching 函数可以调用任何东西。在以下 lambda 闭包中,您调用它的对象是“this”。您可以 return 成功的东西,或者失败的东西。然后你可以将这个结果解压到你的 Result 对象中。 error()require()check() 是在特定条件下抛出异常而不会中断代码流的有用方法。

要return 来自 lambda 外壳的东西,您可以使用 return@nameOfFunctionLambdaIsPassedTo 手动完成,或者让 lambda 的最后一个表达式成为 return 值。 when 表达式可以用作 lambda 的最后(唯一)表达式,因此 when 每个分支中的最后一件事是成功的 returned 结果(或抛出的错误).

这是一个例子:

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    call.runCatching {
        when(method) {
            "getPlatformVersion" -> "Android ${android.os.Build.VERSION.RELEASE}"
            "registerTexture" -> {
                 val entry = texture_registry.createSurfaceTexture()
                 val surfaceTexture = entry.surfaceTexture()
                 val width: Int = argument("width") ?: error("no width passed")
                 val height: Int = argument("height") ?: error("no height passed")
                 surfaceTexture.setDefaultBufferSize(width, height)
                 RendererPlugin.surfaceTextureMap.put(entry, surfaceTexture)
                 check(RendererPlugin.registerSurfaceTextureNativeHandler(entry.id(), surfaceTexture)) {
                     "attention: failed result from registerSurfaceTextureNativeHandler"
                 }
                 mapOf("textureId", entry.id())
            }
            else -> error("unsupported method name: $method")
        }
    }
        .onSuccess(result::success)
        .onFailure(result::failure)
}

如果您想避免调用 onSuccess/onFailure,您可以创建一个可以重复使用的通用扩展函数,如下所示:

inline fun MethodCall.runForResult(result: Result, block: MethodCall.() -> Any) {
    runCatching(block).onSuccess(result::success).onFailure(result::failure)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    call.runForResult(result) {
        when(method) {
            "getPlatformVersion" -> "Android ${android.os.Build.VERSION.RELEASE}"
            "registerTexture" -> {
                 val entry = texture_registry.createSurfaceTexture()
                 val surfaceTexture = entry.surfaceTexture()
                 val width: Int = argument("width") ?: error("no width passed")
                 val height: Int = argument("height") ?: error("no height passed")
                 surfaceTexture.setDefaultBufferSize(width, height)
                 RendererPlugin.surfaceTextureMap.put(entry, surfaceTexture)
                 check(RendererPlugin.registerSurfaceTextureNativeHandler(entry.id(), surfaceTexture)) {
                     "attention: failed result from registerSurfaceTextureNativeHandler"
                 }
                 mapOf("textureId", entry.id())
            }
            else -> error("unsupported method name: $method")
        }
    }
}