如何禁止自定义类型被放入Vec、Rc等容器中?
实现不可放入容器的自定义类型
Rust没有原生语法直接标记类型“不可被放入Vec、Rc这类容器”,但可以通过两种编译期可行的方式实现需求,无需依赖Pin的间接引用:
方法一:将类型设为动态大小类型(DST)
Vec、Rc等容器要求元素必须是Sized(固定大小),我们可以让自定义类型成为动态大小类型(DST),这样容器无法持有它,编译时直接报错。
示例代码:
// 通过末尾的未固定长度数组字段,让类型成为DST struct ImmovableObject<T>(T, [()]); impl<T> ImmovableObject<T> { // 提供构造函数,返回Box包裹的实例(DST无法直接在栈上创建) fn new(value: T) -> Box<Self> { Box::new(ImmovableObject(value, [])) } } fn main() { // 编译错误:the trait `Sized` is not implemented for `ImmovableObject<{integer}>` let mut x = vec![ImmovableObject(42, [])]; // 正确用法:通过Box持有实例,无法放入Vec let obj = ImmovableObject::new(42); }
方法二:私有字段+Pin包裹的构造函数
把类型的字段设为私有,仅提供返回Pin<Box<Self>>的构造函数,同时用PhantomPinned标记类型为!Unpin。用户无法直接创建裸实例,只能持有Pin包裹的Box,自然没法将类型本身放进容器。
示例代码:
use core::marker::PhantomPinned; use core::pin::Pin; struct ImmovableObject<T> { value: T, _pin: PhantomPinned, // 自动标记类型为!Unpin } impl<T> ImmovableObject<T> { // 仅返回Pin<Box<Self>>,用户无法获取裸实例 fn new(value: T) -> Pin<Box<Self>> { Box::pin(Self { value, _pin: PhantomPinned, }) } // 提供方法访问内部值 fn get(&self) -> &T { &self.value } } // 显式禁止Unpin实现(PhantomPinned已自动实现,此步可选) impl<T> !Unpin for ImmovableObject<T> {} fn main() { // 编译错误:cannot construct `ImmovableObject<{integer}>` due to private fields let mut x = vec![ImmovableObject { value:42, _pin: PhantomPinned }]; // 正确用法 let obj = ImmovableObject::new(42); println!("{}", obj.get()); }
局限性说明
如果你的需求是允许用户直接创建栈上实例,但禁止放入容器,目前Rust没有完美的编译期解决方案——因为Rust的移动语义是默认行为,容器依赖这种移动能力。这种情况下只能用运行时检查(比如在Drop中验证对象地址是否从未改变),但无法在编译期阻止违规操作。
内容的提问来源于stack exchange,提问作者vacuum_tube
相关产品推荐
相关产品推荐

