#language #artifact #cross-platform #ios #bindings #traits #android

bin+lib rsbind

提供工具将 Rust 特性绑定到其他语言,并直接导出库工件。就像用本地语言编写的那样调用 Rust 函数。

6 个版本 (破坏性更新)

0.6.0 2022年11月12日
0.5.0 2022年3月30日
0.4.0 2022年3月18日
0.3.0 2022年3月13日
0.1.0 2022年3月8日

#306过程宏

每月下载量 24 次

Apache-2.0

600KB
9K SLoC

包含 (JAR 文件,60KB) gradle-wrapper.jar,(JAR 文件,55KB) gradle-wrapper.jar

Test crates.io

Discord | QQ

Rsbind

Rsbind 提供工具将 Rust 特性绑定到其他语言并直接导出库工件。它还帮助您像在本地语言中编写的那样调用 Rust 服务。
它从 Rust 包生成绑定并将其打包为 Android aar、iOS lib 或其他库工件。您不需要使用此工具编写 JNI 或其他 FFI 代码。

该工具可能对希望将 Rust 用作跨平台语言并为每个平台导出多个工件的人很有用。

快速入门

假设您正在用 Rust 编写两个服务,并在 iOS 和 Android 中调用它们。
首先,您需要编写几个代表您服务的 Rust 特性。

pub trait Services: Send + Sync {
    fn get_login_service() -> Box<dyn LoginService>;
    fn get_upload_service() -> Box<dyn UploadService>;
}

pub trait LoginService: Send + Sync {
    fn login(&self, user_name: String, pwd: String) -> Box<dyn Future>;
}

pub trait Future: Send + Sync {
    fn get(&self) -> bool;
}

pub trait UploadService: Send + Sync {
    fn upload(&self, path: String, listener: Box<dyn UploadProgress>) -> i64;
}

pub trait UploadProgress : Send + Sync {
    fn on_progress(&self, id: i64, process: i64, total: i64);
}

然后,您可以实现您的特性以实现服务。

pub struct ServiceHolder {}

impl Services for ServiceHolder {
    fn get_login_service() -> Box<dyn LoginService> {
        Box::new(LoginServiceImp {})
    }

    fn get_upload_service() -> Box<dyn UploadService> {
        Box::new(UploadServiceImp {})
    }
}

pub struct LoginServiceImp {}

impl LoginService for LoginServiceImp {
    fn login(&self, user_name: String, pwd: String) -> Box<dyn Future> {
        struct FutureImp {
            pub user_name: String,
            pub pwd: String,
        }
        impl Future for FutureImp {
            fn get(&self) -> bool {
                let handle = thread::spawn(move || {
                    // do your login
                    true
                });
                handle.join().unwrap()
            }
        }
        Box::new(FutureImp { user_name, pwd })
    }
}

pub struct UploadServiceImp {}

impl UploadService for UploadServiceImp {
    fn upload(&self, path: String, listener: Box<dyn UploadProgress>) -> i64 {
        thread::spawn(move || {
            // doing uploading
            listener.on_progress(99999, 10, 12345);
        });

        99999
    }
}

之后,运行 rsbind 命令生成 iOS 和 Android 库工件。

rsbind . android all
rsbind . ios all

然后,使用 iOS 库,您可以直接从 Swift 调用服务。

let loginService = RustLib.newServices().getLoginService();
let future = loginService.login(user_name: "sidney.wang", pwd: "88888888")
let result = future.get();
print("login result = \(result)")

class Listener : UploadProgress {
    func onProgress(id: Int64, process: Int64, total: Int64) {
        print("Progress is \(process)/\(total)")
    }
}
let uploadService = RustLib.newServices().getUploadService();
uploadService.upload(path: "to/your/path", listener: Listener())

在 Android 中,操作非常类似,只需运行 Java 代码。

LoginService loginService = RustLib.newServices().getLoginService();
Future future = loginService.login("sidney.wang", "88888888");
boolean result = future.get();
Log.i(TAG, "login result is " + result);

UploadService uploadService = RustLib.newServices().getUploadService();
uploadService.upload("to/your/path", new UploadProgress() {
    @Override
    public void onProgress(long id, long process, long total) {
        Log.i(TAG, "upload process is " + process);
    }
});

逐步进行。

  1. 设置 Rust 环境.
  2. 安装 'rsbind'。 cargo install --git https://github.com/rs-bind/rsbind.git --force -- rsbind
  3. 创建一个 Rust 库,其中包含用于公开您的服务的模块。
  • 结构:Rsbind 模块 rsbind 模块是您公开服务的地方。在这个结构中,API 和实现都在一个模块中,rsbind 将解析所有特性和 impl,并生成绑定代码。您需要将不必要的代码移到其他文件中。

也许您想将 API 和实现分开,那么您可以使用这些结构。

  • 第一个结构
    alt First structure picture
  • 第二结构
    alt Second structure picture

您可以将接口放入合同模块,实现放入imp模块。在lib.rs中公开这两个模块。

// such as your code in contract dir as below:
pub trait YourContract : Send + Sync {
    fn test_simple(arg1: i32, arg2: String) -> String;
    fn test_callback(arg: Box<dyn Callback>);
    fn test_struct() -> StructSimple;
    fn test_return_callback() -> Box<dyn Callback>;
}

pub trait Callback : Send + Sync {
    fn on_callback(&self, arg1: i64, arg2: String);
}

pub struct StructSimple {
    pub arg3: String,
    pub arg4: bool,
}
// Your implementation is as below
pub struct YourImplemetation {}

impl YourContract for YourImplemetation {
    fn test_simple(arg1: i32, arg2: String) -> String {
        format!("Your test_simple result is {}_{}", arg1, arg2)
    }

    fn test_callback(arg: Box<dyn Callback>) {
        arg.on_callback(123i64, "hello callback".to_owned());
    }

    fn test_struct() -> StructSimple {
        StructSimple {
            arg1: "struct".to_owned(),
            arg2: true
        }
    }

    fn test_return_callback() -> Box<dyn Callback> {
        struct Instance{}
        impl Callback for Instance {
            fn on_callback(&self, arg1: i64, arg2: String) {

            }
        }
        Box::new(Instance{})
    }
}
  1. 按照以下方式运行rsbind命令。然后生成的代码将在_gen目录下,aar/framework将在target目录下。

Rsbind用法

rsbind path-of-project android/ios/mac/jar/all  ast/bridge/artifact/header/build/all
  • ast: 使用json格式生成简化的ast文件到_gen/ast。
  • bridge: 生成C方法,以将我们的接口暴露到_gen/[ios/android/mac/jar]_bridge。
  • artifact: 生成java/swift包装器和C头文件,然后将它们放入项目(_gen/[ios/android/mac/jar]_artifact)中。
  • build: 构建桥接模块,将输出复制到artifact项目,然后构建artifact项目。
  • all: 运行绑定步骤的所有步骤。
  1. 它将生成包含在aar或cocoapods库中的java文件,然后您可以将它们集成到您的android/iOS项目中并调用函数。对于android,您可以像下面这样调用
YourContract instance = RustLib.newYourContract();
instance.testCallback(new Callback(){
       void onCallback(long arg1, String arg2) {
           // do your things.
       }
})

Swift非常相似。

配置

您可以为添加一些配置创建一个名为Rsbind.toml的文件。

[android]
rustc_param = ""
arch = ["armv7-linux-androideabi", "aarch64-linux-android", "i686-linux-android"]
release = true
namespace = "com.afoxer.xxx.ffi"
so_name = "demo"
ext_lib = []
features_def = ["xxxx=[]"]

[ios]
rustc_param = ""
arch = ["armv7-apple-ios", "i386-apple-ios", "x86_64-apple-ios"]
release = true
features_def = []

[mac]
rustc_param = ""
release = true
features_def = []

[jar]
rustc_param = ""
release = true
namespace = "com.afoxer.xxx.ffi"
so_name = "demo"
#ext_lib = []
#features_def = ["xxxx=[]"]

支持的类型

  • 参数:基本类型,回调,Vec
  • 返回:基本类型,结构体,回调,Vec

回调中支持的类型

  • 参数:基本类型,Vec,结构体
  • 返回:基本类型,Vec。

将在近期支持它。

定义回调和普通特质不同。它应该在每个回调中包含&self,但不包含在普通特质中。

回调

pub trait Callback : Send + Sync {
    fn on_callback(&self, arg1: i32, arg2: String, arg3: bool, arg4: f32, arg5: f64) -> i32;
    fn on_callback2(&self, arg1: bool) -> bool;
    fn on_callback_complex(&self, arg1: StructSimple) -> bool;
    fn on_callback_arg_vec(&self, arg1: Vec<StructSimple>) -> bool;
    fn on_callback_arg_vec_simple(&self, arg1: Vec<String>) -> bool;
}

普通特质

pub trait TestContract1 : Send + Sync {
    fn test_arg_vec(arg: Vec<String>) -> i32;
    fn test_return_vec(arg: u8) -> Vec<i32>;
    fn test_arg_callback(arg: Box<dyn Callback>) -> u8;
    fn test_bool(arg1: bool) -> bool;
    fn test_struct() -> StructSimple;
    fn test_struct_vec() -> Vec<StructSimple>;
}

依赖项

~13–24MB
~354K SLoC