鍍金池/ 教程/ Java/ 生存期
While 循環(huán)
宏命令
模式
Rust 嵌入到其他語言
變量綁定
if
發(fā)布通道
Lang 項(xiàng)目
匹配
文檔
棧和堆
不依賴 stdlib
原始指針
條件編譯
type 別名
關(guān)聯(lián)類型
全類型
詞匯表
基本類型
Hello, world!
測試
箱和模塊
字符串
向量
引用與借用
所有權(quán)
內(nèi)斂函數(shù)
基準(zhǔn)測試
Nightly Rust
for 循環(huán)
特征
特征的對象
鏈接參數(shù)
介紹
'Deref'強(qiáng)制轉(zhuǎn)換
枚舉
內(nèi)聯(lián)匯編
泛型
方法語法
函數(shù)
外部函數(shù)接口
盒語法和模式
安裝 Rust
unsafe    
生存期
切片模式
Borrow 和 AsRef
If let
學(xué)習(xí) Rust
“常量”和“靜態(tài)”
語法和語義
迭代器
相關(guān)學(xué)術(shù)研究
通用函數(shù)調(diào)用語法
哲學(xué)家就餐問題
類型轉(zhuǎn)換
閉包
并發(fā)性
Hello, Cargo!
屬性
注釋
結(jié)構(gòu)體
編譯器插件
高效 Rust
相關(guān)常量
猜謎游戲
可變性
錯誤處理
新手入門
操作符和重載

生存期

這篇指南是 Rust 已經(jīng)存在的三個所有權(quán)制度之一。這是 Rust 最獨(dú)特和最令人信服的一個特點(diǎn),Rust 開發(fā)人員應(yīng)該相當(dāng)熟悉。所有權(quán)即 Rust 如何實(shí)現(xiàn)其最大目標(biāo)和內(nèi)存安全。這里有幾個不同的概念,每一個概念都有它自己的章節(jié):

  • 所有權(quán),即正在讀的這篇文章。

  • 借用,和與它們相關(guān)的功能‘引用’

  • 生存期,借用的先進(jìn)理念

這三篇文章相關(guān)且有序。如果你想完全的理解所有權(quán)制度,你將需要這三篇文章。

在我們了解細(xì)節(jié)之前,這里有關(guān)于所有權(quán)制度的兩個重要說明需要知道。

Rust 注重安全和速度。它通過許多‘零成本抽象’來完成這些目標(biāo),這意味著在 Rust 中,用盡可能少的抽象成本來保證它們正常工作。所有權(quán)制度是一個零成本抽象概念的一個主要例子。我們將在這篇指南中提到的所有分析都是在編譯時完成的。你不用為了任何功能花費(fèi)任何運(yùn)行成本。

然而,這一制度確實(shí)需要一定的成本:學(xué)習(xí)曲線。許多新用戶使用我們喜歡稱之為‘與借檢查器的人斗爭’,即 Rust 編譯器拒絕編譯那些作者認(rèn)為有效的程序的 Rust 經(jīng)驗(yàn)。這往往因?yàn)槌绦騿T對于所有權(quán)的怎樣工作與 Rust 實(shí)現(xiàn)的規(guī)則不相符的心理模型而經(jīng)常出現(xiàn)。你可能在第一次時會經(jīng)歷類似的事情。然而有個好消息:更有經(jīng)驗(yàn)的 Rust 開發(fā)者報(bào)告稱,一旦他們遵從所有權(quán)制度的規(guī)則工作一段時間后,他們會越來越少的與借檢查器的行為斗爭。

學(xué)習(xí)了這些后,讓我們來了解生存期。

生存期

借出一個對于其他人已經(jīng)擁有的資源的引用會很復(fù)雜。例如,假設(shè)這一系列的操作:

  • 我獲得某種資源的一個句柄。

  • 我借給你對于這個資源的引用。

  • 我決定我使用完了這個資源,然后釋放它,然而你仍然擁有這個引用。

  • 你決定使用資源。

你的引用指向一個無用的資源。當(dāng)資源是內(nèi)存時,這被稱為懸掛指針或者‘釋放后再利用’。

要解決此類問題,我們必須確保在第三步后一定不會發(fā)生第四步。Rust 的所有權(quán)制度通過被稱為生存期的一章來實(shí)現(xiàn),生存期用來介紹一個引用的有效的作用域。

當(dāng)我們有一個函數(shù)將一個引用作為參數(shù)時,我們可以用隱式和顯式兩種方式來表示引用的生存期:

// implicit
fn foo(x: &i32) {
}

// explicit
fn bar<'a>(x: &'a i32) {
}

'a 讀‘生存期為 a ’。從技術(shù)上講,每個引用都有一些與之關(guān)聯(lián)的生存期,但是編譯器允許你在一般情況下忽略它們。但是在此之前,以下是我們分解顯式例子的代碼:

fn bar<'a>(...)

這一部分聲明了我們的生存期。這說明 bar 有一個生存期 'a。如果我們有兩個引用的參數(shù),以下是相關(guān)代碼:

fn bar<'a, 'b>(...)

然后在我們的參數(shù)列表中,我們使用我們已經(jīng)命名的生存期。

...(x: &'a i32)

如果我們想要一個 &mut 引用,我們可以書寫以下代碼:

...(x: &'a mut i32) 

如果你將 &mut i32&'a mut i32 比較,它們是相同的,只是在 &mut i32 之間多了一個 'a。我們將 &mut i32 讀作‘對于 i32 的一個可變引用’,將 &'a mut i32讀作‘對于 i32 的一個生存期為'a 的一個可變引用’。

當(dāng)你操作結(jié)構(gòu)體時,也需要顯式的生存期:

struct Foo<'a> {
x: &'a i32,
}

fn main() {
let y = &5; // this is the same as `let _y = 5; let y = &_y;`
let f = Foo { x: y };

println!("{}", f.x);
}

正如你所看到的的,結(jié)構(gòu)體也可以有生存期。與函數(shù)相似的方式,

struct Foo<'a> {

聲明一個生存期,和以下代碼

x: &'a i32,

使用它。所以,為什么我們在這里需要一個生存期?我們需要確保對 Foo 的任何引用都不能比對它包含的 i32 的引用的壽命長。

作用域的考慮

考慮生存期的一種方式是將一個引用的有效作用域可視化。例如:

fn main() {
let y = &5; // -+ y goes into scope
//  |
// stuff//  |
//  |
}   // -+ y goes out of scope

在我們的 Foo 中添加:

struct Foo<'a> {
x: &'a i32,
}

fn main() {
let y = &5;   // -+ y goes into scope
let f = Foo { x: y }; // -+ f goes into scope
// stuff  //  |
  //  |
} // -+ f and y go out of scope

我們的 fy 的作用域內(nèi)存活,所以一切正常。如果它不是呢?這個代碼不會有效工作:

struct Foo<'a> {
x: &'a i32,
}

fn main() {
let x;// -+ x goes into scope
  //  |
{ //  |
let y = &5;   // ---+ y goes into scope
let f = Foo { x: y }; // ---+ f goes into scope
x = &f.x; //  | | error here
} // ---+ f and y go out of scope
  //  |
println!("{}", x);//  |
} // -+ x goes out of scope

正如你所看到的,fy 的作用域比 x 的作用域要小。但是,當(dāng)我們運(yùn)行 x = &f.x 時,我們給了 x 一個可以超出其作用域范圍的引用。

命名生存期是給這些作用域命名的一種方式。給東西命名是能不能討論它的第一步。

'static

命名為 ‘static’ 的生存期是一個特殊的生存期。這標(biāo)志著這種東西具有整個程序的生存期。很多的 Rust 程序員在處理字符串時會第一次遇到 'static

let x: &'static str = "Hello, world.";

字符串具有 &'static str 這種類型,是因?yàn)檫@種引用始終存在:它們?nèi)谌氲阶罱K二進(jìn)制的數(shù)據(jù)段中。另一個例子是全局變量:

static FOO: i32 = 5;
let x: &'static i32 = &FOO;

以上代碼是將 i32 加入到二進(jìn)制文件的數(shù)據(jù)段中,其中 x 是它的一個引用。

生存期省略

Rust 在函數(shù)體中支持強(qiáng)大的局部類型推斷,但是它在項(xiàng)目簽名中禁止允許僅僅基于單獨(dú)的項(xiàng)目簽名中的類型推斷。然而,對于人體工學(xué)推理來說被稱為“生存期省略”的一個非常受限的二級推理算法,對于函數(shù)簽名非常適用。它能推斷僅僅基于簽名組件本身而不是基于函數(shù)體,僅推斷生存期參數(shù),并且通過僅僅三個容易記住和明確的規(guī)則來實(shí)現(xiàn),這使得生存期省略成為一個項(xiàng)目簽名的縮寫,而不是引用它之后隱藏包含完整的本地推理的實(shí)際類型。

當(dāng)我們談到生存期省略時,我們使用生存期輸入生存期輸出 這兩個術(shù)語。生存期輸入是與一個函數(shù)的一個參數(shù)結(jié)合的一個生存期,同時一個生存期輸出是一個與函數(shù)的返回值相結(jié)合的一個生存期。例如,以下函數(shù)有一個生存期輸入:

fn foo<'a>(bar: &'a str)

以下函數(shù)有一個生存期輸出:

fn foo<'a>() -> &'a str

以下函數(shù)在兩個位置都有一個生存期:

fn foo<'a>(bar: &'a str) -> &'a str

這里有三個規(guī)定:

  • 在函數(shù)參數(shù)中每個省略的生存期都成為一個獨(dú)特的生存期參數(shù)。

  • 如果僅僅有一個輸入生存期,省略或者不省略,在這個函數(shù)的返回值中,這個生存期被分配給所有的省略的生存期。

  • 如果這里有多個輸入生存期,但是其中之一是 &self 或者 &mut self,這個生存期的 self 被分配給所有省略的生存期輸出。

另外,省略一個生存期輸出是錯誤的。

例子

以下列舉了生存期省略的函數(shù)的一些例子。我們已經(jīng)將每個生存期省略的例子和它的擴(kuò)展形式進(jìn)行了配對。

fn print(s: &str); // elided
fn print<'a>(s: &'a str); // expanded

fn debug(lvl: u32, s: &str); // elided
fn debug<'a>(lvl: u32, s: &'a str); // expanded

// In the preceding example, `lvl` doesn’t need a lifetime because it’s not a
// reference (`&`). Only things relating to references (such as a `struct`
// which contains a reference) need lifetimes.

fn substr(s: &str, until: u32) -> &str; // elided
fn substr<'a>(s: &'a str, until: u32) -> &'a str; // expanded

fn get_str() -> &str; // ILLEGAL, no inputs

fn frob(s: &str, t: &str) -> &str; // ILLEGAL, two inputs
fn frob<'a, 'b>(s: &'a str, t: &'b str) -> &str; // Expanded: Output lifetime is unclear

fn get_mut(&mut self) -> &mut T; // elided
fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded

fn args<T:ToCStr>(&mut self, args: &[T]) -> &mut Command // elided
fn args<'a, 'b, T:ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command // expanded

fn new(buf: &mut [u8]) -> BufWriter; // elided
fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> // expanded
上一篇:模式下一篇:基準(zhǔn)測試