Rust不同模块下字段完全相同的struct如何免样板代码互相转换?
Rust同结构不同模块结构体免手写转换方案
方案1:基于serde的序列化反序列化(最稳妥常用)
适用场景:项目已引入serde,或者接受极小的序列化运行时开销
首先在Cargo.toml中引入依赖:[dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" # 追求更高性能可换成bincode,用法逻辑完全一致 # bincode = "1.3"给两个模块的结构体都派生
Serialize和Deserialize特征:mod a { use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize)] struct A { field1: String, field2: String, } } mod b { use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize)] struct A { field1: String, field2: String, } }封装通用转换逻辑,用声明宏批量生成
From实现:use serde::{Serialize, Deserialize}; fn convert_identical_struct<S: Serialize, T: for<'de> Deserialize<'de>>(src: S) -> T { // 结构完全一致的场景下不会出错,可根据自身需求调整错误处理逻辑 serde_json::from_value(serde_json::to_value(src).unwrap()).unwrap() } macro_rules! impl_identical_struct_convert { ($target: ty, $source: ty) => { impl From<$source> for $target { fn from(src: $source) -> Self { convert_identical_struct(src) } } }; } // 仅需一行即可完成单方向转换实现 impl_identical_struct_convert!(a::A, b::A); // 双向转换再加一行即可 impl_identical_struct_convert!(b::A, a::A);调用方式完全符合需求:
let a: a::A = b_instance.into();或者let a = a::A::from(b_instance);方案2:使用专用转换派生宏
如果你不想引入序列化开销,可以使用专门用于结构体转换的三方库,比如struct-convert:[dependencies] struct-convert = { version = "0.2", features = ["derive"] }给两个结构体派生对应特征后即可直接转换,无需手动列出字段。
方案3:unsafe内存转换(不推荐)
如果你可以保证两个结构体的字段顺序、类型完全一致,并且手动给两个结构体添加#[repr(C)]强制C语言内存布局,可以使用std::mem::transmute完成零成本转换,但该方式存在未定义行为风险,除非对性能要求极高且能完全保证结构一致性,否则不要使用。
示例(风险提示:使用前必须确认内存布局完全一致):mod a { #[repr(C)] struct A { field1: String, field2: String, } } mod b { #[repr(C)] struct A { field1: String, field2: String, } } // 转换实现 impl From<b::A> for a::A { fn from(src: b::A) -> Self { unsafe { std::mem::transmute(src) } } }
内容的提问来源于stack exchange,提问作者Javier Arias
相关产品推荐
相关产品推荐

