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(外部函数接口)进行高性能数据序列化和转换的能力。该库建立在两个强大的生态系统之上:
- Serde - Rust的通用序列化框架
- 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()))
}
}
}
性能提示
- 对于大型数据结构,考虑使用零拷贝类型如
&str
而不是String
- 频繁调用的函数中,可以重用Ruby值而不是每次都创建新值
- 对于特别性能敏感的场景,考虑使用更简单的数据表示形式
完整示例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简化了跨语言数据转换的复杂性,同时保持了高性能。