在Rust中使用&self方法时如何解包Option类型字段?
问题:在共享引用下解包结构体中的Option字段
问题背景
结构体包含一个Option字段,编写了一个通过&self引用访问该字段的方法,但解包Option时触发错误——因为解包会尝试移动共享引用后方的数据,违反Rust的所有权规则。
原代码
enum BOrC { B(String), C(u32) } struct A { x: Option<BOrC> } fn print_enum(v: BOrC) { match v { BOrC::B(string) => println!("{}", string.as_str()), BOrC::C(number) => println!("{}", number), } } impl A { fn foo(&self) { let xx: BOrC = self.x.unwrap(); // 问题所在! print_enum(xx) } } fn main() { let one = A{x : Some(BOrC::C(12))}; let two = A{x : Some(BOrC::B("today".to_string()))}; one.foo(); two.foo() }
错误信息
error[E0507]: cannot move out of `self.x` which is behind a shared reference --> src/main.rs:19:24 | 19 | let xx: BOrC = self.x.unwrap(); | ^^^^^^ -------- `self.x` 因该方法调用被移动 | | | 提示:考虑调用`.as_ref()`或`.as_mut()`来借用类型的内容 | 移动发生是因为`self.x`的类型为`Option<BOrC>`,未实现`Copy` trait | note: `Option::<T>::unwrap`获取接收者`self`的所有权,这会移动`self.x` --> /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/core/src/option.rs:932:25
解决方案
方案1:修改打印函数为接受引用(推荐)
原print_enum函数仅需读取内容打印,不需要获取BOrC的所有权。将参数改为引用类型后,只需借用Option中的值即可,无需移动所有权:
enum BOrC { B(String), C(u32) } struct A { x: Option<BOrC> } // 修改为接受引用参数 fn print_enum(v: &BOrC) { match v { BOrC::B(string) => println!("{}", string.as_str()), BOrC::C(number) => println!("{}", number), } } impl A { fn foo(&self) { // 使用as_ref()将Option<BOrC>转为Option<&BOrC>,解包得到引用 let xx: &BOrC = self.x.as_ref().unwrap(); print_enum(xx); // 更安全的写法(避免unwrap panic): // if let Some(xx) = &self.x { // print_enum(xx); // } } } fn main() { let one = A{x : Some(BOrC::C(12))}; let two = A{x : Some(BOrC::B("today".to_string()))}; one.foo(); two.foo() }
方案2:必须获取所有权时(使用可变引用)
如果业务逻辑确实需要获取BOrC的所有权,需将方法的&self改为&mut self,通过Option::take()取出值(取出后结构体的x会变为None):
impl A { fn foo(&mut self) { // take()取出Option中的值,同时将self.x设为None if let Some(xx) = self.x.take() { print_enum(xx); } } } fn main() { let mut one = A{x : Some(BOrC::C(12))}; let mut two = A{x : Some(BOrC::B("today".to_string()))}; one.foo(); // 调用后one.x变为None two.foo() // 调用后two.x变为None }
关键原理
Option::unwrap()会获取Option的所有权,而共享引用(&self)不允许移动后方的数据,因此直接调用会触发错误。Option::as_ref()可将Option<T>转为Option<&T>,实现只读场景下的安全借用,无需转移所有权。- 若必须获取所有权,只能通过可变引用(
&mut self)修改结构体,取出内部值后对应字段会被清空。
内容的提问来源于stack exchange,提问作者yujaiyu
相关产品推荐
相关产品推荐

