[官文翻译]绑定Flutter和Rust高级的内存安全代码生成器 flutter_rust_bridge - 特性(二)- 零拷贝/流/Dart中的异步

470 阅读3分钟

本文正在参加「金石计划 . 瓜分6万现金大奖」


官网:Introduction - flutter_rust_bridge (cjycode.com)

pub: flutter_rust_bridge | Dart Package (flutter-io.cn)

译时版本:1.49.1

原文链接:


零拷贝

ZeroCopyBuffer<Vec<u8>> (和类似的 ZeroCopyBuffer<Vec<i8>>) 从 Rust 向 Dart 发送数据时不会生成拷贝1。 因此会节省拷贝数据的时间,如果是数据很大的话,这个时间会很长(例如高分辨率图像)。

示例

pub fn draw_tree(tree: Vec<TreeNode>) -> ZeroCopyBuffer<Vec<u8>> { ... }

会转成:

Future<Uint8List> drawTree({required List<TreeNode> tree});

生成的 Dart 代码看上去和没有 ZeroCopyBuffer 的完全一样。 虽然这样,但是内部实现却改变了,这里不会有任何内存拷贝!

备注:如果很对  Future 很感兴趣,可以看下 这里

这个功能现在还不支持 Web ,会拷贝缓存。

Stream / Iterator(流/迭代器)

什么是  Stream (流)?简短来说:一次调用,多次返回; 就像Iterator (迭代器)。

Flutter's Stream 是一个强大的抽象概念。 使用其作为 Rust 函数的返回值时,可以在使用场景中调用函数一次,返回多次。

例如,Rust 可能运行很重的算法,然后每隔 100 秒 ,会找出整个解决方案的一个片段。

这种情况下,它可以马上返回给 Flutter 这个片段,然后 Flutter 可以立即渲染到 UI 中。 因此,用户不需要等待整个算法完成,这之前就可以在用户界面上看到部分结果。

至于细节,带签名的 Rust 函数,如 fn f(sink: StreamSink<T>, ..) -> Result<()> 会转译为 Dart 函数 Stream<T> f(..)

注意:可以一直带有这个 StreamSink ,可自由使用它即使 在 Rust 函数返回它自己之后 。下面的日志示例也展示了这一点(create_log_stream 几乎立刻返回,但是当在后面使用了 StreamSink ,看,一个小时)。

StreamSink 可在任何位置被替换。例如,fn f(a: i32, b: StreamSink<String>) 和 fn f(a: StreamSink<String>, b: i32) 都是有效的。

示例

查看广泛使用流的 日志示例 。

Dart 中的异步

该库生成的函数默认是 异步的 。 所以会看到 fn f(..) -> String 转译为 Future<String> f(..) ,它带有这个有趣的 Future

为什么呢?Flutter UI 是单线程的。如果凭直觉使用同步的方式,就像你会(需要)用通常的 Flutter 绑定,UI 会在 Rust 代码执行时 卡住 。如果 Rust 代码花了 100 毫秒运行一个很重的计算,UI 也会卡住 100 毫秒,然后用户就会不爽了。

另一方面,在 Dart 中使用生成的异步绑定,可以很轻易地直接在 Dart/Flutter 的主 Isolate (线程)中调用函数,并且 Rust 代码不会阻塞 Flutter UI 。

事实上,异步和 Future 在 Flutter/Dart 中无处不在,它有很好的内建支持。 所以不必担心。 ;)

备注:一个常见错误是在 另一个 Dart Isolate(即 线程) 而不是主 Isolate 中调用 Rust 代码。这完全不需要,它只会让你的生活更艰难。 正如上面描述的,即使 Rust 代码计算花了 100 毫秒,异步调用只会花费,看,0.1 毫秒,而且不会阻塞 UI 。


本文正在参加「金石计划 . 瓜分6万现金大奖」