Ruby通过FFI调用Rust函数时serde解析JSON报trailing characters错误
哈哈,这个问题我太熟了!你看错误信息里那个末尾的�(空终止符)就是问题所在——咱们一步步拆解:
问题根源
Ruby的FFI在传递字符串到Rust时,会遵循C语言的字符串规范,自动在字符串末尾追加一个\0(空终止符)来标记字符串结束。但你用to_json生成的JSON本身已经是完整的有效字符串了,这个额外的\0就变成了JSON末尾的无效字符,serde_json严格遵循JSON规范,自然会判定为“trailing characters(末尾多余字符)”并抛出错误。
修复方案
你可以从Rust侧或者Ruby侧入手解决,优先推荐Rust侧的处理方式,更可靠:
方法一:Rust侧用CStr自动去除空终止符
这是最规范的处理方式,因为Rust通过FFI接收的字符串本质是C风格字符串,用std::ffi::CStr来转换会自动识别并截断末尾的\0。
比如你的Rust函数可以改成这样:
use serde::Deserialize; use std::ffi::CStr; use std::os::raw::c_char; #[derive(Debug, Deserialize)] struct NetworkParams { network: Network, } #[derive(Debug, Deserialize)] struct Network { server_address: String, } #[no_mangle] pub extern "C" fn send_it_to_rust(raw: *const c_char) -> /* 你的返回类型 */ { // 把C风格字符串转成Rust字符串,自动去掉末尾空终止符 let c_str = unsafe { CStr::from_ptr(raw) }; let json_str = match c_str.to_str() { Ok(s) => s, Err(e) => { eprintln!("Failed to parse string: {}", e); return /* 返回错误结果 */; } }; // 现在解析就不会有问题了 match serde_json::from_str::<NetworkParams>(json_str) { Ok(params) => { // 处理你的业务逻辑 /* 返回成功结果 */ } Err(e) => { eprintln!("Invalid parameters: {}", e); return /* 返回错误结果 */; } } }
方法二:Ruby侧手动截断空字符(应急用)
如果你暂时不想改动Rust代码,也可以在Ruby侧传递前手动去掉可能的空字符:
my_json_raw_str = { network: { server_address: "my_server.com" } } clean_json = my_json_raw_str.to_json.delete("\0") res = send_it_to_rust(clean_json)
不过这个方法不如Rust侧处理可靠,因为FFI的空终止符是Ruby自动添加的,极端场景下可能出现位置异常,所以只推荐临时应急使用。
额外提醒
serde_json对JSON格式的校验非常严格,哪怕是末尾一个不起眼的空字符,也不符合JSON规范,所以才会触发错误。用C风格字符串的方式处理FFI传递的字符串,是Rust和Ruby/其他语言交互的标准做法,能避免很多类似的编码或字符串边界问题。
内容的提问来源于stack exchange,提问作者Korasatavvaku
相关产品推荐
相关产品推荐

