Rust生命周期javascript
程序中每一个变量都有一个固定的做用域,当超出变量的做用域之后,变量就会被销毁。变量在做用域中从初始化到销毁的整个过程称之为生命周期。
fn main() { let a; // --------------+-- a start { // | let b = 5; // -+-- b start | } // -+-- b over |} // --------------+-- a over
main
函数自己的做用域,另一个是在
main
函数中使用一对
{}
定义了一个内部做用域。第2行代码声明了变量
a
,它的做用域是整个
main
函数,也能够说它的生命周期是从第2行代码到第6行代码。在第4行代码中声明了变量
b
,它的做用域是第4行到第6行。咱们能够发现变量的生命周期是有长短的。
生命周期与借用
rust中的借用是指对一块内存空间的引用。rust有一条借用规则是借用方的生命周期不能比出借方的生命周期还要长。
fn main() { let a; // -------------+-- a start { // | let b = 5; // -+-- b start | a = &b; // | | } // -+-- b over | println!("a: {}", a); // |} // -------------+-- a over
变量b
借给了
变量a
,因此a是借用方,b是出借方。能够发现
变量a
(借用方)的生命周期比
变量b
(出借方)的生命周期长,因而这样作违背了rust的借用规则(借用方的生命周期不能比出借方的生命周期还要长)。由于当b在生命周期结束时,a仍是保持了对b的借用,就会致使a所指向的那块内存空间已经被释放了,那么变量a就会是一个悬垂引用。
error[E0597]: `b` does not live long enough --> src/main.rs:5:13 |5 | a = &b; | ^^ borrowed value does not live long enough6 | }; | - `b` dropped here while still borrowed7 | println!("a:{}", a); | - borrow later used here
fn main() { let a = 1; // -------------+-- a start let b = &a; // -------------+-- b start println!("a: {}", a); // |} // -------------+-- b, a over
函数中的生命周期参数
对于一个参数和返回值都包含引用的函数而言,该函数的参数是出借方,函数返回值所绑定到的那个变量就是借用方。因此这种函数也须要知足借用规则(借用方的生命周期不能比出借方的生命周期还要长)。那么就须要对函数返回值的生命周期进行标注,告知编译器函数返回值的生命周期信息。
i32
的引用类型,返回大的那个数的引用。
fn max_num(x: &i32, y: &i32) -> &i32 { if x > y { &x } else { &y }}
fn main() { let x = 1; // -------------+-- x start let max; // -------------+-- max start { // | let y = 8; // -------------+-- y start max = max_num(&x, &y); // | } // -------------+-- y over println!("max: {}", max); // |} // -------------+-- max, x over
max_num
函数返回的引用生命周期是什么,因此运行报错:
error[E0106]: missing lifetime specifier --> src/main.rs:1:33 |1 | fn max_num(x: &i32, y: &i32) -> &i32 { | ---- ---- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `
函数的生命周期参数声明在函数名后的尖括号<>
里,而后每一个参数名跟在一个单引号'
后面,多个参数用逗号隔开。若是在参数和返回值的地方须要使用生命周期进行标注时,只须要在&
符号后面加上一个单引号'
和以前声明的参数名便可。生命周期参数名能够是任意合法的名称。例如:java
fn max_num<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { if x > y { &x } else { &y }}fn main() { let x = 1; // -------------+-- x start let max; // -------------+-- max start { // | let y = 8; // -------------+-- y start max = max_num(&x, &y); // | } // -------------+-- y over println!("max: {}", max); // |} // -------------+-- max, x over
上面代码对函数的参数和返回值的生命周期进行了标注,用于告诉编译器函数参数和函数返回值的生命周期同样长。在第13行代码对进行调用时,编译器会把变量x的生命周期和变量y的生命周期与函数的生命周期参数创建关联。这里值得注意的是,变量x和变量y的生命周期长短实际上是不同的,那么关联到max_num函数的生命周期参数的长度是多少呢?实际上编译器会取变量x的生命周期和变量y的生命周期的部分,也就是取最短的那个变量的生命周期与创建关联。这里最短的生命周期是变量,因此关联的生命周期就是变量y的生命周期。
max_nummax_nummax_num'a'a重叠'ay'a
error[E0597]: `y` does not live long enough --> src/main.rs:13:27 |13 | max = max_num(&x, &y); | ^^ borrowed value does not live long enough14 | } | - `y` dropped here while still borrowed15 | println!("max: {}", max); | --- borrow later used here
max_num
函数返回值所绑定到的那个变量
max
(借用方)的生命周期是从第10行代码到第16行代码,而
max_num
函数的返回值(出借方)的生命周期是
'a
,
'a
的生命周期又是变量x的生命周期和变量y的生命周期中最短的那个,也就是变量y的生命周期。变量y的生命周期是代码的第12行到第14行。因此这里不知足借用规则(借用方的生命周期不能比出借方的生命周期还要长)。也就是为何编译器会说变量y的生命周期不够长的缘由了。函数的生命周期参数并不会改变生命周期的长短,只是用于编译来判断是否知足借用规则。
fn max_num<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { if x > y { &x } else { &y }}fn main() { let x = 1; // -------------+-- x start let y = 8; // -------------+-- y start let max = max_num(&x, &y); // -------------+-- max start println!("max: {}", max); // |} // -------------+-- max, y, x over
函数存在多个生命周期参数时,须要标注各个参数之间的关系。例如:
fn max_num<'a, 'b: 'a>(x: &'a i32, y: &'b i32) -> &'a i32 { if x > y { &x } else { &y }}fn main() { let x = 1; // -------------+-- x start let y = 8; // -------------+-- y start let max = max_num(&x, &y); // -------------+-- max start println!("max: {}", max); // |} // -------------+-- max, y, x over
'b: 'a
来标注
'a
与
'b
之间的生命周期关系,它表示
'a
的生命周期不能超过
'b
,即函数返回值的生命周期
'a
(借用方)不能超过
'b``(出借方),
'a
也不会超过
'a`(出借方)。
结构体中的生命周期参数
一个包含 引用成员
的结构体,必须保证结构体自己的生命周期不能超过任何一个引用成员
的生命周期。不然就会出现成员已经被销毁以后,结构体还保持对那个成员的引用就会产生悬垂引用。因此这依旧是rust的借用规则即借用方(结构体自己)的生命周期不能比出借方(结构体中的引用成员)的生命周期还要长。所以就须要在声明结构体的同时也声明生命周期参数,同时对结构体的引用成员进行生命周期参数标注。
<>
里,每一个参数名跟在一个单引号
'
后面,多个参数用逗号隔开。在进行标注时,只须要在引用成员的
&
符号后面加上一个单引号
'
和以前声明的参数名便可。生命周期参数名能够是任意合法的名称。例如:
struct Foo<'a> { v: &'a i32}
Foo
的生命周期与成员
v
的生命周期创建一个关联用于编译器进行借用规则判断。
#[derive(Debug)]struct Foo<'a> { v: &'a i32}
fn main() { let foo; // -------------+-- foo start { // | let v = 123; // -------------+-- v start foo = Foo { // | v: &v // | } // | } // -------------+-- v over println!("foo: {:?}", foo); // |} // -------------+-- foo over
上面代码的第14行到15行foo
的生命周期依然没有结束,可是它所引用的变量v
已经被销毁了,所以出现了悬垂引用。编译器会给出报错提示:变量v
的的生命周期不够长。nginx
静态生命周期参数
有一个特殊的生命周期参数叫 static
,它的生命周期是整个应用程序。跟其余生命周期参数不一样的是,它是表示一个具体的生命周期长度,而不是泛指。static
生命周期的变量存储在静态段中。
'static
生命周期,例如:
let s: &'static str = "codercat is a static lifetime.";
let s: &str = "codercat is a static lifetime.";
static
变量的生命周期也是
'static
。
static V: i32 = 123;
fn max_num<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { if x > y { &x } else { &y }}fn main() { let x = 1; // -------------+-- x start let max; // -------------+-- max start { // | static Y: i32 = 8; // -------------+-- Y start max = max_num(&x, &Y); // | } // | println!("max: {}", max); // |} // -------------+-- max, Y, x over
max_num
函数。在代码的第12行定义了一个静态变量,它的生命周期是
'static
。
max_num
函数的生命周期参数
'a
会取变量
x
的生命周期和变量
Y
的生命周期重叠的部分。因此传入
max_num
函数并不会报错。
总结
本文分享自微信公众号 - Rust语言中文社区(rust-china)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。swift