本文正在参加「金石计划 . 瓜分6万现金大奖」
官网:Introduction - flutter_rust_bridge (cjycode.com)
pub: flutter_rust_bridge | Dart Package (flutter-io.cn)
译时版本:1.49.1
原文链接:
- Zero copy - flutter_rust_bridge (cjycode.com)
- Stream / Iterator - flutter_rust_bridge (cjycode.com)
- Async in Dart - flutter_rust_bridge (cjycode.com)
零拷贝
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 很感兴趣,可以看下 这里 。
1
这个功能现在还不支持 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万现金大奖」