Rust序列化库serde_magnus的使用:为Ruby FFI提供高性能的Serde绑定与数据转换

Rust序列化库serde_magnus的使用:为Ruby FFI提供高性能的Serde绑定与数据转换

serde_magnus使用Serde和Magnus在Rust和Ruby数据结构之间进行转换。

使用

从Rust到Ruby的转换

serde_magnus::serialize函数将实现了serde::Serialize特性的Rust类型转换为Ruby的等效数据结构。

use serde::{Serialize, Deserialize};
use magnus::{eval, Value};
use serde_magnus::serialize;

// 定义可序列化的Rust数据结构
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Post {
    title: String,
    content: String,
    author: Author,
    tags: Vec<String>
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Author {
    name: String,
    email_address: String
}

// 创建Rust结构体实例
let post = Post {
    title: "Spring carnival planning update".into(),
    content: "Here's what's new.".into(),
    author: Author {
        name: "Martha".into(),
        email_address: "martha@example.com".into()
    },
    tags: vec![
        "carnival".into(),
        "update".into()
    ]
};

// 序列化为Ruby对象
let post: Value = serialize(&post)?;

// 在Ruby中验证转换结果
assert!(eval!(
    r#"
    post == {
      title: "Spring carnival planning update",
      content: "Here's what's new.",
      author: {
        name: "Martha",
        email_address: "martha@example.com"
      },
      tags: ["carnival", "update"]
    }
    "#,
    post
)?);

从Ruby到Rust的转换

serde_magnus::deserialize将Ruby值转换为实现了serde::Deserialize特性的Rust类型。

use magnus::RHash;
use serde_magnus::deserialize;

// 从Ruby代码获取一个Hash对象
let post: RHash = eval!(r#"
  {
    title: "Spring carnival planning update",
    content: "Here's what's new.",
    author: {
      name: "Martha",
      email_address: "martha@example.com"
    },
    tags: ["carnival", "update"]
  }
"#)?;

// 反序列化为Rust结构体
let post: Post = deserialize(post)?;

// 验证转换结果
assert_eq!(
    Post {
        title: "Spring carnival planning update".into(),
        content: "Here's what's new.".into(),
        author: Author {
            name: "Martha".into(),
            email_address: "martha@example.com".into(),
        },
        tags: vec![
            "carnival".into(),
            "update".into()
        ]
    },
    post
);

完整示例

下面是一个更完整的Rust与Ruby交互示例,展示了双向数据转换的实际应用:

use serde::{Serialize, Deserialize};
use magnus::{eval, value::ReprValue, Error, RHash, Value};
use serde_magnus::{serialize, deserialize};

// 定义用户数据结构
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct User {
    id: u32,
    name: String,
    email: String,
    is_admin: bool,
    permissions: Vec<String>
}

fn main() -> Result<(), Error> {
    // 初始化Ruby解释器
    magnus::Ruby::init(|ruby| {
        // 示例1: Rust到Ruby的转换
        let user = User {
            id: 42,
            name: "Alice".into(),
            email: "alice@example.com".into(),
            is_admin: true,
            permissions: vec!["read".into(), "write".into()]
        };
        
        // 序列化为Ruby对象
        let ruby_user: Value = serialize(&user)?;
        
        // 在Ruby中验证
        assert!(eval!(
            r#"
            user == {
              id: 42,
              name: "Alice",
              email: "alice@example.com",
              is_admin: true,
              permissions: ["read", "write"]
            }
            "#,
            ruby_user
        )?);
        
        // 示例2: Ruby到Rust的转换
        let ruby_hash: RHash = eval!(r#"
          {
            id: 123,
            name: "Bob",
            email: "bob@example.com",
            is_admin: false,
            permissions: ["read"]
          }
        "#)?;
        
        // 反序列化为Rust结构体
        let rust_user: User = deserialize(ruby_hash)?;
        
        // 验证转换结果
        assert_eq!(
            User {
                id: 123,
                name: "Bob".into(),
                email: "bob@example.com".into(),
                is_admin: false,
                permissions: vec!["read".into()]
            },
            rust_user
        );
        
        Ok(())
    })
}

系统要求

serde_magnus需要Rust 1.65+和Ruby 3.0+版本。

许可证

serde_magnus采用MIT许可证发布。


1 回复

Rust序列化库serde_magnus的使用:为Ruby FFI提供高性能的Serde绑定与数据转换

介绍

serde_magnus是一个Rust库,它提供了在Rust和Ruby之间通过FFI(外部函数接口)进行高性能数据序列化和转换的能力。该库建立在两个强大的生态系统之上:

  1. Serde - Rust的通用序列化框架
  2. Magnus - 用于创建Ruby扩展的Rust库

serde_magnus的主要特点:

  • 允许Rust和Ruby之间无缝传递复杂数据结构
  • 高性能的数据转换,避免了不必要的复制
  • 支持大多数常见的数据类型和自定义类型
  • 简化了Ruby扩展中数据处理的复杂性

安装

在Cargo.toml中添加依赖:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_magnus = "0.1"
magnus = "0.5"

基本使用方法

1. 基本类型转换

use magnus::{rb_assert, Ruby};
use serde_magnus::serialize;

fn example(ruby: &Ruby) -> Result<(), magnus::Error> {
    // 将Rust基本类型转换为Ruby对象
    rb_assert!(ruby, "value == 42", serialize(&42)?);
    rb_assert!(ruby, "极客时间", serialize(&3.14)?);
    rb_assert!(ruby, "value == true", serialize(&true)?);
    rb_assert!(ruby, "value == 'hello'", serialize(&"hello")?);
    
    Ok(())
}

2. 复杂数据结构转换

use magnus::{rb_assert, Ruby};
use serde::{Serialize, Deserialize};
use serde_magnus::{serialize, deserialize};

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Person {
    name: String,
    age: u32,
    hobbies: Vec<String>
}

fn example(ruby: &Ruby)极客时间 Result<(), magnus::Error> {
    let person = Person {
        name: "Alice".to_string(),
        age: 30,
        hobbies: vec!["hiking".to_string(), "reading".to_string()]
    };
    
    // 将Rust结构体序列化为Ruby Hash
    let ruby_value = serialize(&person)?;
    rb_assert!(ruby, "value[:name] == 'Alice'", ruby_value);
    rb_assert!(ruby, "value[:age] == 30", ruby_value);
    
    // 从Ruby Hash反序列化为Rust结构体
    let ruby_hash = ruby.eval(r#"{ name: "Bob", age: 25, hobbies: ["swimming"] }"#)?;
    let person: Person = deserialize(ruby_hash)?;
    assert_eq!(person.name, "Bob");
    
    Ok(())
}

3. 在Ruby扩展中使用

use magnus::{function, Ruby};
use serde_magnus::serialize;

#[derive(serde::Serialize)]
struct Response {
    status: u16,
    body: String,
    headers: Vec<(String, String)>
}

#[magnus::init]
fn init(ruby: &Ruby) -> Result<(), magnus::Error> {
    ruby.define_global_function("process_data", function!(process_data, 1))?;
    Ok(())
}

fn process_data(input: String) -> magnus::Value {
    let ruby = Ruby::get().unwrap();
    
    let response = Response {
        status: 200,
        body: format!("Processed: {}", input),
        headers: vec![
            ("Content-Type".to_string(), "application/json".to_string())
        ]
    };
    
    serialize(&response).unwrap()
}

然后在Ruby中可以这样使用:

require 'my_extension'

result = process_data("test input")
puts result[:status]  # => 200
puts result[:body]    # => "Processed: test input"

高级用法

自定义类型转换

use magnus::{rb_assert, Ruby, IntoValue};
use serde::{Serialize, Deserialize};
use serde_magnus::{serialize, deserialize};

#[derive(Serialize, Deserialize)]
#[serde(remote = "std::time::SystemTime")]
struct SystemTimeDef {
    secs: i64,
    nanos: u32,
}

#[derive(Serialize, Deserialize)]
struct Event {
    name: String,
    #[serde(with = "SystemTimeDef")]
    timestamp: std::time::SystemTime,
}

fn example(ruby: &Ruby) -> Result<(), magnus::Error> {
    let event = Event {
        name: "user.login".to_string(),
        timestamp: std::time::SystemTime::now(),
    };
    
    let ruby_value = serialize(&event)?;
    rb_assert!(ruby, "value.is_a?(Hash)", ruby_value);
    rb_assert!(ruby, "value[:name] == 'user.login'", ruby_value);
    
    Ok(())
}

错误处理

use magnus::{Ruby, Error};
use serde_magnus::{serialize, deserialize};

fn process_user_input(ruby: &Ruby, input: magnus::Value) -> Result<(), Error> {
    match deserialize::<String>(input) {
        Ok(s) => {
            println!("Received string: {}", s);
            Ok(())
        },
        Err(e) => {
            Err(Error::new(ruby.exception_type_error(), e.to_string()))
        }
    }
}

性能提示

  1. 对于大型数据结构,考虑使用零拷贝类型如&str而不是String
  2. 频繁调用的函数中,可以重用Ruby值而不是每次都创建新值
  3. 对于特别性能敏感的场景,考虑使用更简单的数据表示形式

完整示例demo

下面是一个完整的Ruby扩展示例,展示了如何使用serde_magnus在Rust和Ruby之间传递复杂数据:

// lib.rs
use magnus::{function, Ruby};
use serde::{Serialize, Deserialize};
use serde_magnus::{serialize, deserialize};

#[derive(Serialize, Deserialize, Debug)]
struct User {
    id: u64,
    username: String,
    email: String,
    preferences: Vec<String>,
    is_active: bool,
}

// 从Ruby Hash创建User
fn create_user(rb_user: magnus::Value) -> magnus::Value {
    let ruby = Ruby::get().unwrap();
    
    match deserialize::<User>(rb_user) {
        Ok(user) => {
            println!("Deserialized user: {:?}", user);
            serialize(&user).unwrap()
        },
        Err(e) => {
            let error = Error::new(ruby.exception_type_error(), e.to_string());
            ruby.raise(error).unwrap()
        }
    }
}

// 生成示例User
fn generate_user() -> magnus::Value {
    let user = User {
        id: 123,
        username: "rust_lover".to_string(),
        email: "rust@example.com".to_string(),
        preferences: vec!["dark_mode".to_string(), "notifications".to_string()],
        is_active: true,
    };
    
    serialize(&user).unwrap()
}

#[magnus::init]
fn init(ruby: &Ruby) -> Result<(), magnus::Error> {
    ruby.define_global_function("create_user", function!(create_user, 1))?;
    ruby.define_global_function("generate_user", function!(generate_user, 0))?;
    Ok(())
}

Ruby使用示例:

require_relative 'my_extension'

# 从Ruby创建用户
user = {
  id: 456,
  username: "ruby_dev",
  email: "ruby@example.com",
  preferences: ["light_mode"],
  is_active: false
}

created = create_user(user)
puts "Created user: #{created}"

# 生成示例用户
generated = generate_user
puts "Generated user: #{generated}"

总结

serde_magnus为Rust和Ruby之间的数据交换提供了强大而灵活的工具,特别适合:

  • 构建高性能的Ruby扩展
  • 在Rust和Ruby之间共享复杂数据结构
  • 需要类型安全的数据序列化场景

通过结合Serde的序列化能力和Magnus的Ruby互操作性,serde_magnus简化了跨语言数据转换的复杂性,同时保持了高性能。

回到顶部