You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何禁止自定义类型被放入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 17:35:30