#dart #flutter #bindings #codegen #zero-copy #ffi #memory-management

membrane

Membrane 是一个有观点的 crate,可以从 Rust 库生成 Dart 包。通过 bincode 在 FFI 边界实现严格类型和零拷贝返回,提供极高的性能。

20 个版本 (8 个破坏性更新)

0.11.0 2023年9月15日
0.9.6 2023年7月21日
0.6.3 2022年5月20日
0.6.2 2022年3月25日
0.3.3 2021年11月17日

168开发工具 中排名

Download history 10042/week @ 2024-04-20 14128/week @ 2024-04-27 17291/week @ 2024-05-04 17051/week @ 2024-05-11 35933/week @ 2024-05-18 28410/week @ 2024-05-25 33725/week @ 2024-06-01 35350/week @ 2024-06-08 31646/week @ 2024-06-15 35385/week @ 2024-06-22 23705/week @ 2024-06-29 14157/week @ 2024-07-06 11019/week @ 2024-07-13 10791/week @ 2024-07-20 11028/week @ 2024-07-27 10596/week @ 2024-08-03

46,163 每月下载量

Apache-2.0

115KB
3K SLoC

Membrane

Membrane 是一个有观点的 crate,可以从你的 Rust 库生成 Dart 包。它提供极高性能,具有严格类型、自动内存管理和通过 bincode 在 FFI 边界实现零拷贝返回。

Membrane diagram

开发环境

在 Linux 上,ffigen 会查找位于 /usr/lib/llvm-11/lib/libclang.so 的 libclang,因此你可能需要将其链接到特定版本的库:ln -s /usr/lib/llvm-11/lib/libclang.so.1 /usr/lib/llvm-11/lib/libclang.so

使用方法

查看 示例 目录中的可运行示例。

在你的 crate 的 lib.rs 中添加一个 RUNTIME 静态变量,该变量将存在于整个程序的生命周期中。 RUNTIME 必须持有 membrane::App<Runtime> 实例,其中 Runtime 为你希望使用的任何异步框架实现了 membrane::Interface 特性。在我们的示例中,我们使用 tokio 来提供运行时行为,欢迎你复制它

use membrane::runtime::{App, Interface, AbortHandle};

pub struct Runtime(tokio::runtime::Runtime);

impl Interface for Runtime {
  fn spawn<F>(&self, future: F) -> AbortHandle
  where
    F: std::future::Future + Send + 'static,
    F::Output: Send + 'static,
  {
    let handle = self.0.spawn(future);
    AbortHandle {
      abort: Box::new(move || handle.abort()),
    }
  }

  fn spawn_blocking<F, R>(&self, future: F) -> AbortHandle
  where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
  {
    let handle = self.0.spawn_blocking(future);
    AbortHandle {
      abort: Box::new(move || handle.abort()),
    }
  }
}

static RUNTIME: App<Runtime> = App::new(|| {
  Runtime(
    tokio::runtime::Builder::new_multi_thread()
    .worker_threads(2)
    .thread_name("libexample")
    .build()
    .unwrap()
  )
});

然后编写一些带有 #[async_dart] 宏注释的代码。这里不需要使用 C 类型,只需使用 Rust 的 Stringi64f64bool、结构体或枚举(或使用 Option)即可。函数可以放在程序中的任何位置,并可能返回一个异步的 Result<T, E> 或一个 impl Stream<Item = Result<T, E>>

use membrane::async_dart;
use tokio_stream::Stream;

use crate::data;

#[async_dart(namespace = "accounts")]
pub fn contacts() -> impl Stream<Item = Result<data::Contact, data::Error>> {
  futures::stream::iter(vec![Ok(Default::default())])
}

#[async_dart(namespace = "accounts")]
pub async fn contact(id: String) -> Result<data::Contact, data::Error> {
  Ok(data::Contact {
    id: id.parse().unwrap(),
    ..Default::default()
  })
}

现在您已经准备好生成 Dart 包了。请注意,这段代码应该放在 bin/generator.rs 或类似的文件中,并使用 cargo run 或构建任务来运行,而不是在 build.rs 中(该文件仅在编译前运行)。

fn main() {
  // if nothing else in this generator.rs references lib.rs then
  // at least call a dummy function so lib.rs doesn't get optimized away
  example::load();

  let mut project = membrane::Membrane::new();
  project
    // name the output pub directory
    .package_destination_dir("../dart_example")
    // the pub package name, if different than the directory
    .package_name("example")
    // give the basename of the .so or .dylib that your Rust program provides
    .using_lib("libexample")
    // use Dart enums instead of class enums
    .with_c_style_enums(true)
    .create_pub_package()
    .write_api()
    .write_c_headers()
    .write_bindings();
}

如果一切按计划进行,您现在可以从 Dart 中调用 Rust:

cd example
cargo run
cargo build
cd ../dart_example
cp ../example/target/debug/libexample.dylib .
dart --enable-asserts run

(--enable-asserts 启用生成类中的漂亮打印 toString())

import 'package:dart_example/accounts.dart';

void main(List<String> arguments) async {
  var accounts = AccountsApi();
  print(await accounts.contact(id: "1"));
}

如果在 Linux 上遇到无法加载 libexample.so 的错误,请将 pub 包的路径添加到 LD_LIBRARY_PATH

依赖项

~7–14MB
~158K SLoC