如何实现支持trait对象的MyBox,使Vec<MyBox<dyn MyTrait>>可用?
为什么自定义MyBox无法像Box一样存储trait对象?
你遇到的问题核心在于Rust不会自动允许自定义指针类型转换为指向trait对象的指针,而标准库的Box<T>之所以能做到,是因为它自动实现了CoerceUnsized这个关键trait,同时编译器对它有内置支持。
关键原因:CoerceUnsized trait
CoerceUnsized是Rust中负责处理指针类型强制转换的trait,专门用于将指向 Sized 类型的指针(比如Box<MyStruct>)转换为指向 Unsized 类型的指针(比如Box<dyn MyTrait>)。标准库中的Box<T>、Rc<T>、Arc<T>等智能指针都默认实现了这个trait,因此编译器允许它们进行这类转换。
而你的MyBox<T>没有实现CoerceUnsized,所以编译器无法将MyBox<MyStruct>推导为MyBox<dyn MyTrait>,直接抛出类型不匹配的错误。
解决方法:手动实现必要的trait
要让MyBox支持trait对象存储,需要手动实现CoerceUnsized和Deref(后者保证trait对象能正常解引用调用方法)。注意CoerceUnsized目前是不稳定特性,需要使用Rust Nightly版本并开启相关feature:
#![feature(coerce_unsized)] #![feature(ptr_metadata)] use std::ops::Deref; use std::ptr::NonNull; use std::marker::Unsize; use std::ops::CoerceUnsized; trait MyTrait {} struct MyStruct {} impl MyTrait for MyStruct {} struct MyBox<T: ?Sized> { value: NonNull<T> } impl<T> MyBox<T> { pub fn new(value: T) -> MyBox<T> { unsafe { MyBox { value: NonNull::new_unchecked(Box::into_raw(Box::new(value))) } } } } // 实现Deref,让MyBox拥有和Box一致的解引用行为 impl<T: ?Sized> Deref for MyBox<T> { type Target = T; fn deref(&self) -> &Self::Target { unsafe { self.value.as_ref() } } } // 实现CoerceUnsized,允许MyBox<T>转换为MyBox<U>(当T可以转换为U时) impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<MyBox<U>> for MyBox<T> {} // 现在可以正常运行 fn main_mybox() { let mut v: Vec<MyBox<dyn MyTrait>> = Vec::new(); let x: MyBox<MyStruct> = MyBox::new(MyStruct {}); v.push(x); } // 原Box示例不受影响 fn main_box() { let mut v: Vec<Box<dyn MyTrait>> = Vec::new(); let x: Box<MyStruct> = Box::new(MyStruct {}); v.push(x); }
补充说明:
Unsizetrait是编译器自动实现的,当一个类型可以转换为某个unsized类型时(比如MyStruct实现了MyTrait,因此可以转换为dyn MyTrait),会自动为该类型实现Unsize<dyn MyTrait>。- 实现
Deref是为了让MyBox的实例能像Box一样被解引用,这样trait对象的方法调用才能正常工作。 - 由于
CoerceUnsized尚未稳定,如果你需要在稳定版Rust中实现类似功能,目前没有直接的替代方案,只能依赖标准库提供的智能指针或者使用其他间接方式。
内容的提问来源于stack exchange,提问作者Ian Melendez
相关产品推荐
相关产品推荐

