如何在Rust中创建包含依赖引用的结构体Baz?
如何在Rust中创建包含依赖引用的结构体Baz?
你碰到的这个问题是Rust所有权系统里非常典型的场景——咱们想让Baz同时持有Foo实例和依赖该Foo引用的Bar实例,但编译器直接抛出了两个报错,核心原因是Rust没法保证你内部引用的有效性,咱们一步步拆解下:
为什么原来的代码会报错?
在Baz::new方法里,你先创建了局部变量foo,接着用这个局部变量的引用创建了Bar,然后又把foo移动到Baz的foo字段中。这里有两个核心问题:
- 引用生命周期不匹配:
Bar持有的引用指向的是局部变量foo,但这个局部变量随后被移动到了Baz里,原来的局部内存位置已经失效了,编译器没法保证这个引用在Baz的生命周期内一直有效,所以抛出了cannot return value referencing local variable的错误。 - 移动后再借用:你先把
foo移动到结构体,之后又试图引用它,而Foo没有实现Copytrait,移动后原变量就不能再被使用,所以出现了borrow of moved value的错误。
解决办法(按推荐程度排序)
1. 使用引用计数智能指针(Rc/Arc)共享所有权
这是最常用也最安全的方案,单线程场景用Rc,多线程场景用Arc。通过让Bar持有Rc<Foo>而不是&Foo,我们可以让Foo被多个所有者共享,这样就不需要担心生命周期的问题了——只要还有Rc引用存在,Foo就不会被销毁。
修改后的代码示例:
use std::rc::Rc; struct Foo {} struct Bar { foo: Rc<Foo>, } struct Baz { foo: Rc<Foo>, bar: Bar, } impl Foo { fn new() -> Self { Self {} } } impl Bar { fn new(foo: Rc<Foo>) -> Self { Self { foo } } } impl Baz { fn new() -> Self { let foo = Rc::new(Foo::new()); Self { foo: Rc::clone(&foo), // 这里只是复制引用,不会克隆Foo实例 bar: Bar::new(foo), } } }
2. 如果允许,让Bar持有Foo的所有权
如果你的Bar不需要共享Foo,只是需要使用它,那可以直接让Bar持有Foo的所有权,这样Baz里就只需要放Bar,或者调整结构让Foo属于Bar、Baz持有Bar。不过这个方案只适用于你能修改Bar定义的情况。
3. 避免内部引用,重新设计结构
如果不能修改第三方库的Bar(必须用引用),那你只能把Foo放在Baz外面,让Baz持有Foo的引用和Bar,比如:
struct Foo {} struct Bar<'a> { foo: &'a Foo, } struct Baz<'a> { foo: &'a Foo, bar: Bar<'a>, } impl Foo { fn new() -> Self { Self {} } } impl<'a> Bar<'a> { fn new(foo: &'a Foo) -> Self { Self { foo } } } impl<'a> Baz<'a> { fn new(foo: &'a Foo) -> Self { Self { foo, bar: Bar::new(foo), } } }
但这样Baz的生命周期就完全依赖外部的Foo,使用起来会有很多限制,不太推荐。
4. 不安全代码(不推荐)
你也可以用unsafe来绕过编译器的检查,比如用UnsafeCell或者直接裸指针,但这会失去Rust的安全保障,很容易出现悬垂引用等内存安全问题,除非你对Rust的内存模型非常熟悉,否则绝对不要这么做。
备注:内容来源于stack exchange,提问作者mfurseman
相关产品推荐
相关产品推荐

