Rust中如何将已校验类型相同的dyn trait转换为泛型T?
错误原因
std::mem::transmute 的大小校验是编译期执行的,不会参考你写的运行时TypeId判断逻辑:
- 泛型函数的合法性校验是在定义阶段执行,不是等到实例化阶段才做。编译器需要保证函数对所有满足约束的
T都能合法编译,而你的T只有'static + ?Sized约束,编译器无法确定Box<T>的大小一定和Box<dyn Interface>一致:- 当
T是dyn Trait这类动态大小类型时,Box<T>是包含数据指针+虚表指针的胖指针,64位平台下占128位 - 当
T是普通Sized类型时,Box<T>是单指针,占64位
- 当
- 你手动实例化
T = dyn Interface时能通过类型检查,只是说明该特定实例合法,但无法证明泛型定义对所有T都合法,因此编译器还是会在泛型定义阶段拦截transmute调用。
解决方案
不要用transmute做转换,改用原始指针中转的方式,你已经通过TypeId校验确认T和目标dyn Trait是完全相同的类型,这个转换的安全性是可控的:
fn get_override_for_interface<T: 'static + ?Sized>() -> Option<Box<T>> { // 实际使用时可以替换成从全局注册表匹配所有注册的dyn Trait TypeId的逻辑 if std::any::TypeId::of::<T>() == std::any::TypeId::of::<dyn Interface>() { let value: Box<dyn Interface> = Box::new(Implementation{}); // 先把Box转成原始指针,强制类型转换后再转回到Box let ptr = Box::into_raw(value) as *mut T; return Some(unsafe { Box::from_raw(ptr) }); } // 其他dyn Trait的匹配逻辑可以扩展在这里 None }
多接口扩展思路
如果要支持任意已注册的dyn Trait类型,可以用全局静态注册表存储所有覆写:
- 用
std::collections::HashMap<TypeId, *mut ()>作为存储结构,key是dyn Trait的TypeId,value是对应的Box<dyn Trait>原始指针 - 提供配套的注册接口,调用时将
Box<dyn Trait>转成原始指针存入注册表 get_override_for_interface函数中拿T的TypeId去注册表匹配,命中就用上面的指针中转方式返回Box<T>
内容的提问来源于stack exchange,提问作者reinis_mx
相关产品推荐
相关产品推荐

