如何让子结构体持有父结构体某一字段的引用?
你遇到的问题本质是Rust借用规则的核心约束:当一个值被可变或不可变借用时,你不能移动它的所有权。原代码里,child先借用了s,随后parent尝试获取s的所有权,这直接触发了借用检查器的错误——因为此时s的借用还处于活跃状态。
下面是几种可行的解决方案,按推荐程度排序:
1. 使用Rc共享所有权(推荐,安全)
如果你的场景是单线程的,最安全的方式是用std::rc::Rc来包裹String,让Parent和Child共享对这个字符串的所有权,这样就不需要依赖生命周期借用了。
修改结构体和代码如下:
use std::rc::Rc; pub struct Parent { s: Rc<String>, child: Child, } pub struct Child { s: Rc<String>, } fn main() { let s = Rc::new(String::from("hello")); // 克隆Rc只是增加引用计数,不会复制底层字符串 let child = Child { s: Rc::clone(&s) }; let parent = Parent { s, child }; // 验证两者指向同一个字符串 assert_eq!(parent.s, parent.child.s); }
Rc的clone()操作非常轻量,只是在堆上的引用计数加1,底层的字符串只会被创建一次,当最后一个Rc被销毁时,字符串才会被释放。
2. 使用Arc支持多线程
如果你的代码需要在多线程环境下运行,把Rc换成std::sync::Arc即可(Arc是原子引用计数,线程安全),用法和Rc几乎一致:
use std::sync::Arc; pub struct Parent { s: Arc<String>, child: Child, } pub struct Child { s: Arc<String>, } // 多线程场景示例 use std::thread; fn main() { let s = Arc::new(String::from("thread-safe")); let child = Child { s: Arc::clone(&s) }; let parent = Parent { s, child }; let parent_clone = Parent { s: Arc::clone(&parent.s), child: Child { s: Arc::clone(&parent.s) }, }; thread::spawn(move || { println!("{}", parent_clone.s); }).join().unwrap(); }
3. 不安全的unsafe代码(不推荐,需谨慎)
如果你确定自己能保证内存安全,可以用unsafe绕过借用检查器,但这会失去Rust的内存安全保障,需要自己确保Parent的生命周期始终长于Child,且字符串不会被提前释放。
示例代码:
pub struct Parent { s: String, child: Child, } // Child用裸指针代替引用 pub struct Child { s: *const String, } fn main() { let s = String::from("unsafe example"); // 先创建Parent,再给Child的指针赋值 let mut parent = Parent { s, child: Child { s: std::ptr::null() }, }; // 获取Parent中s的指针 parent.child.s = &parent.s as *const String; // 使用时需要转成引用,注意unsafe块 unsafe { println!("{}", *parent.child.s); } }
⚠️ 注意:这种方式非常危险,如果后续代码中Parent被移动或者s被修改/销毁,Child的指针会变成悬垂指针,导致未定义行为。除非万不得已,否则不要使用这种方法。
为什么原代码不行?
再回头看你的原代码:
let s = String::new(); let child = Child { s: &s }; let parent = Parent { child, s };
当你创建child时,&s是对栈上s的不可变借用,此时s的借用生命周期会延续到child存在的整个期间。随后你尝试把s移动到parent里,这相当于把s从栈的一个位置移到另一个位置,原来的借用地址就失效了——Rust的借用检查器正是为了防止这种悬垂引用的出现,所以直接报错。
内容的提问来源于stack exchange,提问作者Mendy

