Rust FFI工具库Diplomat的使用:简化跨语言接口生成与安全互操作

Rust FFI工具库Diplomat的使用:简化跨语言接口生成与安全互操作

介绍

Diplomat是一个实验性的Rust工具,用于生成FFI定义,允许许多其他语言调用Rust代码。使用Diplomat,您可以简单地定义要通过FFI公开的Rust API,并自动获得高级的C、C++和JavaScript绑定!

Diplomat支持从Rust生成以下语言的绑定:

  • C
  • C++
  • Dart
  • JavaScript/TypeScript
  • Kotlin (使用JNA)
  • Python (使用nanobind)

安装

首先,安装用于生成绑定的CLI工具:

$ cargo install diplomat-tool

然后,将Diplomat宏和运行时作为依赖项添加到您的项目中:

diplomat = "0.10.0"
diplomat-runtime = "0.10.0"

完整示例

以下是一个完整的Diplomat使用示例,展示如何创建一个Rust库并通过FFI暴露给其他语言:

  1. 首先创建一个Rust库项目:
$ cargo new --lib my_ffi_lib
$ cd my_ffi_lib
  1. 编辑Cargo.toml添加依赖:
[package]
name = "my_ffi_lib"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
diplomat = "0.10.0"
diplomat-runtime = "0.10.0"
  1. 创建src/lib.rs内容:
use diplomat_core::ffi::*;

#[diplomat::bridge]
mod ffi {
    pub struct Person {
        pub name: String,
        pub age: u32,
    }

    impl Person {
        /// 创建一个新Person
        pub fn new(name: &str, age: u32) -> Person {
            Person {
                name: name.to_string(),
                age,
            }
        }

        /// 获取用户信息
        pub fn get_info(&self) -> String {
            format!("{} is {} years old", self.name, self.age)
        }

        /// 让用户年龄增长一岁
        pub fn have_birthday(&mut self) {
            self.age += 1;
        }
    }
}
  1. 生成FFI绑定:
$ diplomat-tool generate c --out-dir ./c_b bindings
$ diplomat-tool generate cpp --out-dir ./cpp_bindings
$ diplomat-tool generate js --out-dir ./js_bindings
  1. 构建项目:
$ cargo build --release

现在您可以在目标语言中使用生成的绑定来调用Rust代码。例如在C++中:

#include "my_ffi_lib.hpp"
#include <iostream>

int main() {
    auto person = my_ffi_lib::Person::new_("Alice", 30);
    std::cout << person.get_info() << std::endl;  // 输出: Alice is 30 years old
    
    person.have_birthday();
    std::cout << person.get_info() << std::endl;  // 输出: Alice is 31 years old
    
    return 0;
}

或者在JavaScript中:

const { Person } = require('./js_bindings/my_ffi_lib');

let person = Person.new("Bob", 25);
console.log(person.getInfo());  // 输出: Bob is 25 years old

person.haveBirthday();
console.log(person.getInfo());  // 输出: Bob is 26 years old

注意事项

对于wasm32-unknown-unknown目标的JavaScript绑定,Diplomat假设您正在C规范ABI上构建WebAssembly。在最新的Rust版本中,这不是默认设置,因此您有两个选项:

  1. 使用nightly Rust构建并启用-Zwasm-c-abi=spec标志
  2. 配置JS后端使用传统绑定。WASM ABI有一个配置选项

1 回复

Rust FFI工具库Diplomat:简化跨语言接口生成与安全互操作

完整示例Demo

下面是一个完整的Rust项目示例,展示如何使用Diplomat创建跨语言接口:

1. 创建Rust库项目

cargo new --lib my_diplomat_demo
cd my_diplomat_demo

2. 修改Cargo.toml

[package]
name = "my_diplomat_demo"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
diplomat = "0.4"

3. 实现Rust库代码

// src/lib.rs
use diplomat::ffi;

#[diplomat::bridge]
mod ffi {
    /// 表示一个数学计算器
    pub struct Calculator {
        value: f64,
    }

    impl Calculator {
        /// 创建新的计算器实例
        pub fn new(initial_value: f64) -> Calculator {
            Calculator { value: initial_value }
        }

        /// 获取当前值
        pub fn get_value(&self) -> f64 {
            self.value
        }

        /// 加法运算
        pub fn add(&mut self, operand: f64) -> Result<(), CalculatorError> {
            self.value = self.value
                .checked_add(operand)
                .ok_or(CalculatorError::Overflow)?;
            Ok(())
        }

        /// 乘法运算
        pub fn multiply(&mut self, operand: f64) -> Result<(), CalculatorError> {
            self.value = self.value
                .checked_mul(operand)
                .ok_or(CalculatorError::Overflow)?;
            Ok(())
        }
    }

    /// 计算器错误类型
    #[derive(Debug)]
    pub enum CalculatorError {
        Overflow,
        DivisionByZero,
    }
}

4. 添加绑定生成工具

// src/bin/generate_bindings.rs
use std::path::PathBuf;

fn main() {
    let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
    
    diplomat::run(
        diplomat::Config {
            out_dir: &out_dir,
            target_languages: &[diplomat::TargetLanguage::C],
            ..Default::default()
        },
        &["src/lib.rs"],
    ).unwrap();
}

5. 构建项目并生成绑定

cargo build

6. 生成的C头文件示例

// calculator.h
#include <stdint.h>
#include <stddef.h>

typedef struct Calculator Calculator;

typedef enum CalculatorError {
    CalculatorError_Overflow,
    CalculatorError_DivisionByZero,
} CalculatorError;

Calculator* Calculator_new(double initial_value);
double Calculator_get_value(const Calculator* self);
CalculatorError Calculator_add(Calculator* self, double operand);
CalculatorError Calculator_multiply(Calculator* self, double operand);
void Calculator_destroy(Calculator* self);

7. C语言使用示例

// main.c
#include "calculator.h"
#include <stdio.h>

int main() {
    // 创建计算器实例
    Calculator* calc = Calculator_new(10.0);
    
    // 进行加法运算
    CalculatorError err = Calculator_add(calc, 5.0);
    if (err != 0) {
        printf("Addition error: %d\n", err);
        return 1;
    }
    
    // 进行乘法运算
    err = Calculator_multiply(calc, 3.0);
    if (err != 0) {
        printf("Multiplication error: %d\n", err);
        return 1;
    }
    
    // 获取结果
    double result = Calculator_get_value(calc);
    printf("Final result: %f\n", result);
    
    // 清理资源
    Calculator_destroy(calc);
    return 0;
}

8. 编译和运行C程序

gcc main.c -L./target/debug -lmy_diplomat_demo -o calculator_demo
./calculator_demo

这个完整示例展示了:

  1. 如何定义Rust结构体和方法
  2. 如何处理错误并跨语言传递
  3. 如何生成C语言绑定
  4. 如何在C语言中使用生成的绑定
  5. 内存管理的最佳实践

通过这个示例,您可以清楚地看到Diplomat如何简化Rust与其他语言的互操作过程。

回到顶部