下面的代码不能编译,因为x
在 move 之后使用(因为x
有 type &mut u8
,它没有实现Copy
trait)
fn main() {
let mut a: u8 = 1;
let x: &mut u8 = &mut a;
let y = x;
x;
}
Run Code Online (Sandbox Code Playgroud)
据我所知,y
隐式具有类型&mut u8
但是如果我明确指定y
它编译的类型。以下代码编译
fn main() {
let mut a: u8 = 1;
let x: &mut u8 = &mut a;
let y: &mut u8 = x;
x;
}
Run Code Online (Sandbox Code Playgroud)
顺便说一句,如果我更改let y: &mut u8 = x;
到let y: &u8 = x;
它还会编译。
在我看来,一切都没有改变,y
是&mut u8
在第一个例子,它是&mut u8
在第二,但前者不编译和后者则编译。
为什么?有什么不同?
&mut u8
不是一个完整的类型。所有引用类型都必须有一个生命周期参数。当你省略它时,Rust 编译器必须推断它,它会选择最短的生命周期。在您的第一个示例中,脱糖的第一步是(使用来自 Rustonomicon 的假语法):
fn main() {
let mut a: u8 = 1;
'a: {
let x: &'a mut u8 = &'a mut a; // the lifetime of x must last until it's use at the end
let y = x;
x;
}
}
Run Code Online (Sandbox Code Playgroud)
这仍然不是完全明确的,因为y
仍然不知道的类型。它是什么?好吧,由于它是从 分配的x
,它是 a &'a mut u8
,它也应该是 a &'a mut u8
。请注意,这不遵循生命周期省略的“可能的最短生命周期”规则。您没有省略一生,而是省略了整个类型,这是通过类型推断重建的。
fn main() {
let mut a: u8 = 1;
'a: {
let x: &'a mut u8 = &'a mut a; // the lifetime of x must last until its use at the end
let y: &'a mut u8 = x;
x;
}
}
Run Code Online (Sandbox Code Playgroud)
嗯,那不好。由于与y
具有相同的生命周期x
,因此其创建涉及将引用移入x
并使其x
无效。因此,该程序因尝试使用而被拒绝x
。
添加签名y
本质上为编译器提供了一个可以推断生命周期的新位置。之前,正常的类型推断与y
具有相同的类型x
,这意味着它会持续很长时间x
并且x
无法使用。现在,y
不需要与x
;具有相同的类型。借用的生命周期可能不同。特别是,它被做得更短。
fn main() {
let mut a: u8 = 1;
'a: {
let x: &'a mut u8 = &'a mut a; // the lifetime of x must last until it's use at the end
'b: {
let y: &'b mut u8 = x; // y and x can now have different lifetimes, *x is reborrowed here
}
x; // x not moved from, still valid
}
}
Run Code Online (Sandbox Code Playgroud)
现在,不是将引用x
移入y
并使其无效,而是将值*x
临时“重新借用”到 make y
,如 by let y: &'b mut u8 = &'b mut *x
。
另一个可能的解决方法是明确地说“*x
以不同的生命周期再次借用”:
fn main() {
let mut a: u8 = 1;
let x: &mut u8 = &mut a;
let y = &mut *x;
x;
}
Run Code Online (Sandbox Code Playgroud)
原理和以前一样:说&
得越频繁,编译器就有更多的地方可以按摩程序中的生命周期,使一切正常。