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