我有以下代码:
struct Bar<T> {
k: [T; 10],
}
impl<T> Bar<T> {
fn thing(&self, i: usize) -> &T {
&self.k[i]
}
fn thing_mut(&mut self, i: usize) -> &mut T {
&mut self.k[i]
}
}
struct Foo<'a, T: 'a> {
bar: &'a Bar<T>,
count: usize,
}
impl<'a, T> Foo<'a, T> {
fn get(&mut self) -> Option<&'a T> {
if self.count < 10 {
let thing = self.bar.thing(self.count);
self.count += 1;
Some(thing)
} else {
None
}
}
}
struct FooMut<'a, T: 'a> {
bar: &'a mut Bar<T>,
count: usize,
}
impl<'a, T> FooMut<'a, T> {
fn get(&mut self) -> Option<&'a mut T> {
if self.count < 10 {
let thing = self.bar.thing_mut(self.count);
self.count += 1;
Some(thing)
} else {
None
}
}
}
Foo
编译,但FooMut
不会:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/main.rs:40:34
|
40 | let thing = self.bar.thing_mut(self.count);
| ^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 38:5...
--> src/main.rs:38:5
|
38 | / fn get(&mut self) -> Option<&'a mut T> {
39 | | if self.count < 10 {
40 | | let thing = self.bar.thing_mut(self.count);
41 | | self.count += 1;
... |
45 | | }
46 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:40:25
|
40 | let thing = self.bar.thing_mut(self.count);
| ^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 37:1...
--> src/main.rs:37:1
|
37 | impl<'a, T> FooMut<'a, T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...so that the expression is assignable:
expected std::option::Option<&'a mut T>
found std::option::Option<&mut T>
为什么不可变的编译得很好,但不是可变的?我在FooMut
案例中缺少一些终身注释吗?我已经看到了很多关于生命和引用的答案,但我在这个案例中特别询问了可变和不可变的案例。
答案 0 :(得分:0)
不可变和可变引用的生命周期方面已在varius地方涵盖: 请参阅答案中嵌入的问题和参考文献的评论。
我在这里写了一些关注这个特定案例的笔记,希望能够揭示Rust生命周期的困难概念 (至少对我来说很难)。
考虑这个片段,这个简化版本暴露了同样的问题:
struct Foo<'a> {
x: &'a mut i32,
}
impl<'b> Foo<'b> {
fn x(&mut self) -> &'b mut i32 { self.x }
}
fn main() {
let y = &mut 5; // <- 'a(1)
let mut f = Foo { x: y }; // 'a(1) <- 'b(2)
println!("x is: {}", f.x()); // 'a(1) 'b(2) <- 'anonymous(3)
}
这里有三个生命周期:
'a(1)
生命周期为&mut i32
'b(2)
生命周期为Foo
'anonymous(3)
生命周期由编译器分配给&self
引用,因为在&self
方法中没有为fn x(&mut self) -> &'b i32
分配明确的生命周期值。在文档中,struct
和impl
上的终身泛型通常使用相同的字母进行注释:
在此示例中,我使用struct
注释了'a
生命周期泛型,并使用impl
注释了'b
,以证明编译器生成的具体生命周期与两个不同的范围相关联。
请参阅上面示例代码中的注释以获得可视化表示。
如果我们尝试编译,我们得到:
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/main.rs:6:30
|
6 | fn x(&self) -> &'b i32 { self.x }
| ^^^^^^
|
note: ...the reference is valid for the lifetime 'b as defined on the impl at 5:1...
--> src/main.rs:5:1
|
5 | impl<'b> Foo<'b> {
| ^^^^^^^^^^^^^^^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 6:5
--> src/main.rs:6:5
|
6 | fn x(&self) -> &'b i32 { self.x }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
我们发现'anonymous
生命周期比'b
更窄(参见&#34; approssimate&#34;代码注释中的生命周期可视化):借来的内容self.x
不够活跃满足防锈安全规则。
现在很明显,解决方案应该是在elision规则的支持下,通过明确的注释或更好的方式缩短生命周期:
struct Foo<'a> {
x: &'a mut i32,
}
impl<'b> Foo<'b> {
fn x(&mut self) -> &mut i32 { self.x }
}
fn main() {
let y = &mut 5; // <- 'a
let mut f = Foo { x: y }; // 'a <- 'b
println!("x is: {}", f.x()); // 'a 'b
}
现在该代码段已编译,此处学到的教训应该是从中复制的短语 这个great answer:
经验法则:不要在任何地方发送垃圾邮件。对于应该相同的事物只使用相同的生命周期
好的,但为什么如果Foo::x
是不可变引用,编译器不会抛出错误?
简短的回答是:
如果内部引用是不可变的,则编译器可以确保不会出现因缩小生命周期范围而导致的内存问题。
如果self.x
是&'a mut i32
引用,并且编译器为了'anonymous
分配了一个&self
生命周期,则编译失败,因为引用的引用要求严格等于生命期在内部可变性的情况下,并且编译器'b
和'anonymous
不等于。
有关详细信息,请参阅this great answer。