Lifetimes¶
在Rust中,对象生命周期和所有权是紧密相关的。所有权规则是Rust的核心特性,也是Rust与其他语言最大的不同之处。
所有权(Ownership)¶
每一块内存数据都和一个变量名绑定,这个变量名就是这块内存数据的所有者。当所有者超出作用域时,这块内存数据就会被释放。在本节中,“变量”和“绑定”都是指通过let
关键字声明的变量。
当使用=
将一个变量赋值给另一个变量,或是将变量作为函数参数传递时,所有权会发生转移,此时有两种可能:
- 值拷贝:原来的内存数据会被复制一份给新的变量,两个变量名绑定的内存数据不同。
- 值移动:原来的内存数据会被重新绑定给新的变量,此时不能再通过原来的变量名访问内存数据。
默认情况下,Rust会采取值移动的方式进行赋值,除非对象的类型实现了Copy
trait。大多数基本类型都实现了Copy
trait。
fn main() {
let a = vec![1, 2, 3]; // vec is not a primitive type, and it does not implement Copy trait
let b = a;
println!("{:?} {:?}", a, b); // Error; use of moved value: `a`
}
引用(Borrowing)¶
在Rust中,变量的引用有两种,即只读引用(&T
)和可变引用(&mut T
)。只读引用和可变引用都是指向原始变量的指针,但只读引用不允许修改原始变量的值,而可变引用允许修改原始变量的值。可变引用只能指向可变变量。需要使用解引用运算符*
来访问引用指向的值。
fn main() {
let const_var = 5;
let mutable_ref = &mut const_var;
// error[E0596]: cannot borrow `const_var` as mutable, as it is not declared as mutable
}
引用的生命周期是从引用声明开始到引用最后一次使用结束。关于引用的使用有如下限制:
-
在引用的生命周期内,可变引用和只读引用不能同时存在。
fn main() { let mut owner = 5; let shared_borrow = &owner; // share_borrow starts here let mutable_borrow = &mut owner; // mutable_borrow starts here println!("{}", mutable_borrow); // Error // mutable_borrow ends here println!("{}", shared_borrow); // share_borrow ends here }
fn main() { let mut owner = 5; let shared_borrow = &owner; // shared_borrow starts here println!("{}", shared_borrow); // shared_borrow ends here // mutable_borrow starts here let mutable_borrow = &mut owner; // mutable_borrow starts here println!("{}", mutable_borrow); // mutable_borrow ends here }
-
在可变引用的生命周期内,不能对同一变量声明新的可变引用。
fn main() { let mut owner = 5; let mutable_borrow = &mut owner; let another_mutable_borrow = &mut owner; println!("{} {}", mutable_borrow, another_mutable_borrow); // error[E0499]: cannot borrow `owner` as mutable more than once at a time }
fn main() { let mut owner = 5; let mutable_borrow = &mut owner; println!("{}", mutable_borrow); let another_mutable_borrow = &mut owner; println!("{}", another_mutable_borrow); }
-
在可变引用的生命周期内,不能访问原始变量或对原始变量进行赋值。
fn main() { let mut owner = 5; let mutable_borrow = &mut owner; println!("{}", owner + *mutable_borrow); // error[E0503]: cannot use `owner` because it was mutably borrowed }
fn main() { let mut owner = 5; let mutable_borrow = &mut owner; println!("{}", *mutable_borrow + owner); // 10 }
-
可以用数据库中“共享锁”和“排他锁”的概念来理解只读引用和可变引用。只读引用相当于对原始变量加共享锁,可变引用相当于对原始变量加排他锁。对于同一块数据,共享锁可以同时存在,但排他锁只能有一个。
-
引用的生命周期不能超过原始变量的生命周期。
struct Foo<'a> { x: Option<&'a u32>, } fn main() { let mut x = Foo { x: None }; { let y = 0; x.x = Some(&y); } // Leaving the scope of y, y is dropped println!("{:?}", x.x) // error[E0597]: `y` does not live long enough }
fn one(x: &i32) -> &i32 { let ret = *x + 1; &ret // error[E0515]: cannot return value referencing local variable `ret` } // Leaving the scope of ret, ret is dropped fn main() { println!("{}", *one(&5)); }
生命周期¶
引用相当于指向一段内存空间的指针,在最后一次访问引用后,引用的生命周期就结束了,但在一些情况下需要继续保持引用的生命周期,比如在函数中返回引用。在这种情况下,需要使用引用标记来指定引用的生命周期。
fn main() {
let reference_to_nothing = dangle();
// error[E0106]: missing lifetime specifier
}
fn dangle() -> &String {
let s = String::from("hello");
&s // The lifetime of s is over, so this reference is dangling
}
引用标记可以用于泛型参数、函数参数、函数返回值,而不能用于引用声明。有相同引用标记的引用的生命周期相同。'static
是一个特殊引用标记,表示引用的生命周期是整个程序的生命周期,即引用指向的内存空间在程序结束时才会被释放。所有指向字符串字面量的引用都是'static
类型。
fn function<'a>() -> &'a str {}
fn function<'a>(x: &'a str) {}
fn function<'a>(x: &'a str) -> &'a str {}
fn function<'a>(x: &'a str, y: &'a str) -> &'a str {}
fn function<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {}
enum Enum<'a> {
Variant1(&'a str),
Variant2(&'a str) // exist as long as parent object exists
}
struct Struct<'a> {
field: &'a str // exist as long as parent object exists
}
struct Struct<'a> {
field: &'a str
}
trait New<'a> {
fn new() -> Self;
}
impl<'a> New<'a> for Struct<'a> {
fn new() -> Self {
Struct { field: "hello" }
}
}
fn function<T>(x: T) where for<'a> T: Fn(&'a str) -> &'a str {}
enum Enum<T> where for<'a> T: Fn(&'a str) -> &'a str { V(T) }
struct Struct<T> where for<'a> T: Fn(&'a str) -> &'a str { field: T }
impl<T> Struct<T> where for<'a> T: Fn(&'a str) -> &'a str {
fn function(&self) -> &F { &self.field }
}
其中,where for
有另一种写法
fn function<T>(x: T) where T: for<'a> Fn(&'a str) -> &'a str {}
for
对T
进行了限制,表示T
必须是一个函数,且这个函数的参数和生命周期必须和模板匹配。
struct Closure<F> {
data: (u8, u16),
func: F,
}
impl<F> Closure<F>
where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8,
{
fn call(&self) -> &u8 {
(self.func)(&self.data)
}
}
fn do_it(data: &(u8, u16)) -> &u8 { &data.0 }
fn main() {
let clo = Closure { data: (0, 1), func: do_it };
println!("{}", clo.call());
}
不同引用标记的引用不能相互赋值,在作为函数返回值时,Rust也会检查引用标记是否匹配。可以用'a: 'b
来约束'a
的生命周期不短于'b
的生命周期。
fn two_args<'a, 'b>(a: &'a mut i32, b: &'b i32) -> &'b mut i32 {
*a += b;
a // error: lifetime may not live long enough
}
fn main() {
let mut a = 3;
let b: &mut i32 = two_args(&mut a, &4);
println!("{}", b);
}
fn two_args<'a, 'b>(a: &'a mut i32, b: &'b i32) -> &'a mut i32 {
*a += b;
a
}
fn main() {
let mut a = 3;
let b: &mut i32 = two_args(&mut a, &4);
println!("{}", b);
}
fn two_args<'a: 'b, 'b>(a: &'a mut i32, b: &'b i32) -> &'b mut i32 {
*a += b;
a
}
fn main() {
let mut a = 3;
let b: &mut i32 = two_args(&mut a, &4);
println!("{}", b);
}
通常情况下,函数声明中的引用标记可以省略,Rust会自动推断每个参数和返回值的引用标记
-
每个输出参数都会分配一个独立的引用标记。
fn four_args(a: &i32, b: &i32, c: &i32, d: &i32) -> i32 { a + b + c + d }
fn four_args<'a, 'b, 'c, 'd>(a: &'a i32, b: &'b i32, c: &'c i32, d: &'d i32) -> i32 { a + b + c + d }
-
如果只有一个输入参数,那么这个参数的引用标记就是(所有)返回值的引用标记。
fn one_arg(a: &i32) -> &i32 { a }
fn one_arg<'a>(a: &'a i32) -> &'a i32 { a }
-
当函数的某个参数为
&self
或&mut self
时,返回值的引用标记和&self
的引用标记相同。struct Player { first_name: String, last_name: String, } impl Player { fn full_name(&self) -> String { format!("{} {}", self.first_name, self.last_name) } }
struct Player { first_name: String, last_name: String, } impl Player { fn full_name<'a>(&'a self) -> String { format!("{} {}", self.first_name, self.last_name) } }
-
如果不满足上述条件,则需要显式指定引用标记。
fn two_args(a: &mut i32, b: &i32) -> &mut i32 { *a += b; a // error[E0106]: missing lifetime specifier } fn main() { let mut a = 3; let b: &mut i32 = two_args(&mut a, &4); println!("{}", b); }
fn two_args<'a, 'b>(a: &'a mut i32, b: &'b i32) -> &'a mut i32 { *a += b; a } fn main() { let mut a = 3; let b: &mut i32 = two_args(&mut a, &4); println!("{}", b); }