如何在枚举中存储带指定生命周期的可变借用?
在枚举变体中存储可变借用的问题
我希望在枚举变体中存储可变借用&mut Foo<'a>,直接使用该可变借用时一切正常,但将其放入枚举后却无法正常编译。简化后的代码如下:
use std::collections::HashMap; #[derive(Debug)] struct Foo<'a> { shared_ref: &'a usize, child_foos: HashMap<usize, Foo<'a>>, } impl<'a> Foo<'a> { // 获取子Foo的“裸”可变引用——可以正常工作! pub fn get_handle(&mut self, id: usize) -> &mut Foo<'a> { self.child_foos.get_mut(&id).unwrap() } // !!! 获取封装在枚举中的子Foo可变引用——无法正常工作 !!! pub fn get_handle_in_enum(&mut self, id: usize) -> Handle { Handle::Foo(self.child_foos.get_mut(&id).unwrap()) } } // 注意:编译器要求使用&'a mut Foo<'a>。问题:为何需要&'a mut(即为何不能使用&mut Foo<'a>)? enum Handle<'a> { Foo(&'a mut Foo<'a>), }
问题1
为什么枚举定义中必须使用&'a mut Foo<'a>?我希望改用&mut Foo<'a>,怀疑这是后续编译错误的根源。
问题2
编译代码时出现了一些令人困惑的错误,其中一条错误中编译器表示“找到了它期望的内容”?错误信息如下:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements --> src/main.rs:39:9 | 39 | Handle::Foo(self.child_foos.get_mut(&id).unwrap()) | ^^^^^^^^^^^ | note: first, the lifetime cannot outlive the anonymous lifetime defined here... --> src/main.rs:38:31 | 38 | pub fn get_handle_in_enum(&mut self, id: usize) -> Handle { | ^^^^^^^^^ note: ...so that the types are compatible --> src/main.rs:39:9 | 39 | Handle::Foo(self.child_foos.get_mut(&id).unwrap()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: expected `Handle<'_>` found `Handle<'_>` note: but, the lifetime must be valid for the lifetime `'a` as defined here... --> src/main.rs:30:6 | 30 | impl<'a> Foo<'a> { | ^^ note: ...so that the expression is assignable --> src/main.rs:39:21 | 39 | Handle::Foo(self.child_foos.get_mut(&id).unwrap()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: expected `&mut Foo<'_>` found `&mut Foo<'a>` For more information about this error, try `rustc --explain E0495`. error: could not compile `enum_mut_borrow` due to previous error
问题解答
针对问题1:生命周期绑定的误区
当你写&mut Foo<'a>时,Rust会自动为这个可变引用分配一个独立的匿名生命周期(比如'b),也就是实际类型是&'b mut Foo<'a>——其中'b是可变引用本身的存活时间,'a是Foo内部shared_ref的存活时间,两者本不需要关联。
但你原来的Handle<'a>只声明了一个生命周期参数,编译器会强制把引用的生命周期'b和Foo的生命周期'a绑定在一起,因此要求你写成&'a mut Foo<'a>。这是一个不必要的强约束,直接导致了后续的生命周期冲突。
正确的做法是给枚举声明两个独立的生命周期参数,明确区分引用的生命周期和Foo内部引用的生命周期:
enum Handle<'ref_life, 'foo_life> { Foo(&'ref_life mut Foo<'foo_life>), }
针对问题2:生命周期冲突的本质
编译错误的核心是生命周期不匹配:
- 函数
get_handle_in_enum中的&mut self带有一个匿名生命周期(比如'b),调用get_mut返回的是&'b mut Foo<'a>——这个引用的存活时间只能和self的借用周期一样长。 - 但原来的
Handle<'a>要求引用的生命周期必须是'a(和Foo内部的shared_ref一致),而'b是函数调用的临时生命周期,必然短于'a,因此编译器无法满足这个矛盾的要求,抛出错误。
那个看似矛盾的expected Handle<'_>, found Handle<'_>提示,本质是编译器在推断时,一方面期望Handle的生命周期是'b(和&mut self一致),另一方面又因为枚举的旧定义被迫要求是'a,两个'_代表的是不同的匿名生命周期,所以出现了这种“字面一致但实际不同”的情况。
修正后的完整代码
use std::collections::HashMap; #[derive(Debug)] struct Foo<'a> { shared_ref: &'a usize, child_foos: HashMap<usize, Foo<'a>>, } impl<'a> Foo<'a> { // 获取子Foo的可变引用——正常工作 pub fn get_handle(&mut self, id: usize) -> &mut Foo<'a> { self.child_foos.get_mut(&id).unwrap() } // 获取封装在枚举中的子Foo可变引用——修正后正常工作 pub fn get_handle_in_enum(&mut self, id: usize) -> Handle<'_, 'a> { Handle::Foo(self.child_foos.get_mut(&id).unwrap()) } } // 两个独立生命周期:'ref_life是可变引用的生命周期,'foo_life是Foo内部引用的生命周期 enum Handle<'ref_life, 'foo_life> { Foo(&'ref_life mut Foo<'foo_life>), }
内容的提问来源于stack exchange,提问作者Corvus
相关产品推荐
相关产品推荐

