Rust中外层结构体存储内层结构体引用的惯用方式及堆分配咨询
问题背景
我当前使用的代码如下:
struct Inner { value1: i32, value2: i32, } pub struct Outer<'a> { vectors: Vec<&'a Inner>, } impl<'a> Outer<'a> { pub fn new() -> Self { let inner = Inner { value1: 0, value2: 0, }; let mut vectors = Vec::new(); vectors.push(&inner); Outer { vectors: vectors } } }
运行时出现以下错误:
无法返回引用局部变量
inner的值
返回的值引用了当前函数拥有的数据experiment.rs(17, 22):
inner在此处被借用
我需要分配一个非常大的Inner对象(例如数百MB的位图),希望直接在堆上创建它,现咨询以下问题:
- 在Rust中实现此需求最惯用的方式是使用Box::new还是Rc<>?
- 经搜索得知似乎必须通过这两种方式包装结构体才能实现,此结论是否正确?
- Vector显然不需要此类包装,如何让我的结构体更像Vector,无需包装即可存储在堆上?
问题解答
1. 惯用方式选择
如果你的Inner对象不需要被多个所有者共享,优先用Box::new——这是Rust里最基础、最惯用的堆分配方式,没有额外的引用计数开销,语义也最清晰:就是把单个值放到堆上,由Box独占所有权。
如果需要多个地方同时持有这个大对象的所有权(比如多个Outer或者其他结构体都要引用它,且不想手动管理生命周期),再考虑用Rc<>。但如果只是单纯要把大对象放堆上,Box完全足够,没必要上Rc。
2. 是否必须用包装类型?
这个结论不完全正确。
Rust里的普通结构体默认是栈分配,但如果结构体内部包含了堆分配的成员(比如Vec、String、Box),那结构体本身虽然在栈上,但它的核心数据是在堆上的。不过如果你的Inner是纯栈分配类型(比如只包含i32这类基本类型),那要把整个结构体放到堆上,确实需要用Box或者其他智能指针包装——因为Rust没有语法直接把普通结构体标记为堆分配。
但反过来想:如果你的Inner本身就包含大的堆分配数据(比如你的位图可以用Vec<u8>存储),那即使Inner本身在栈上,它的大内存还是在堆上,这时候也可以不用Box包装。不过你要的是“直接在堆上创建整个Inner对象”,那还是得靠Box这类包装。
3. 让结构体像Vec一样无需包装就在堆上?
Vec之所以看起来不用包装就存堆上,是因为它本身就是一个智能指针类型:Vec的栈上部分只是一个包含指针、长度、容量的小结构体,真正的数据在堆上。
如果你想让自己的结构体也做到这点,有两种思路:
- 把结构体内部的大数据用堆分配类型承载:比如你的位图用
Vec<u8>作为Inner的成员,这样Inner的栈部分很小,大内存自动在堆上,不需要额外包装。 - 自定义类似Vec的智能指针:用
unsafe代码实现一个自定义的堆分配结构体,手动管理堆内存(比如模仿Box的实现),但这种方式复杂度高,除非有特殊需求,否则不推荐——直接用Box或者让结构体包含堆分配成员就够了。
另外,你原来代码里的错误本质是生命周期问题:你把栈上的inner的引用放进Vec然后返回,栈上的inner在函数结束后就销毁了,引用自然失效。换成Vec<Box<Inner>>之后,就能直接持有所有权,不会有生命周期问题。
内容的提问来源于stack exchange,提问作者Parth

