欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

rust生命周期詳解

 更新時(shí)間:2023年03月17日 10:49:57   作者:zy010101  
生命周期是rust中用來規(guī)定引用的有效作用域,在大多數(shù)時(shí)候,無需手動(dòng)聲明,因?yàn)榫幾g器能夠自動(dòng)推導(dǎo),這篇文章主要介紹了rust生命周期相關(guān)知識,需要的朋友可以參考下

rust生命周期

生命周期是rust中用來規(guī)定引用的有效作用域。在大多數(shù)時(shí)候,無需手動(dòng)聲明,因?yàn)榫幾g器能夠自動(dòng)推導(dǎo)。當(dāng)編譯器無法自動(dòng)推導(dǎo)出生命周期的時(shí)候,就需要我們手動(dòng)標(biāo)明生命周期。生命周期主要是為了避免懸垂引用。

借用檢查

rust的編譯器會使用借用檢查器來檢查我們程序的借用正確性。例如:

#![allow(unused)]
fn main() {
{
    let r;

    {
        let x = 5;
        r = &x;
    }

    println!("r: {}", r);
}
}

在編譯期,Rust 會比較兩個(gè)變量的生命周期,結(jié)果發(fā)現(xiàn) r 明明擁有生命周期 'a,但是卻引用了一個(gè)小得多的生命周期 'b,在這種情況下,編譯器會認(rèn)為我們的程序存在風(fēng)險(xiǎn),因此拒絕運(yùn)行。

函數(shù)中的生命周期

#![allow(unused)]
fn main() {
fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
}

執(zhí)行這段代碼,rust編譯器會報(bào)錯(cuò),它給出的help信息如下:

help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`

意思是函數(shù)返回類型是一個(gè)借用值,但是無法從函數(shù)的簽名中得知返回值是從x還是y借用的。并且給出了相應(yīng)的修復(fù)代碼。

4 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str
  |           ++++     ++          ++          ++

按照這個(gè)提示,我們更改函數(shù)聲明。就會發(fā)現(xiàn)可以順利通過編譯。因此,像這樣的函數(shù),我們無法判斷它是返回x還是y,那么只好手動(dòng)進(jìn)行生命周期聲明。上面的提示就是手動(dòng)聲明聲明周期的語法。

手動(dòng)聲明生命周期

需要注意的是,標(biāo)記的生命周期只是為了取悅編譯器,讓編譯器不要難為我們,它不會改變?nèi)魏我玫膶?shí)際作用域

生命周期的語法是以’開頭,名稱往往是一個(gè)單獨(dú)的小寫字母。大多數(shù)人用’a來作為生命周期的名稱。如果是引用類型的參數(shù),生命周期會位于&之后,并用空格來將生命周期和參數(shù)分隔開。函數(shù)簽名中的生命周期標(biāo)注和泛型一樣,需要在提前聲明生命周期。例如我們剛才修改過的函數(shù)簽名

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str

該函數(shù)簽名表明對于某些生命周期 'a,函數(shù)的兩個(gè)參數(shù)都至少跟 'a 活得一樣久,同時(shí)函數(shù)的返回引用也至少跟 'a 活得一樣久。實(shí)際上,這意味著返回值的生命周期與參數(shù)生命周期中的較小值一致:雖然兩個(gè)參數(shù)的生命周期都是標(biāo)注了 'a,但是實(shí)際上這兩個(gè)參數(shù)的真實(shí)生命周期可能是不一樣的(生命周期 'a 不代表生命周期等于 'a,而是大于等于 'a)。例如:

fn main() {
    let string1 = String::from("long string is long");

    {
        let string2 = String::from("xyz");
        let result = longest(string1.as_str(), string2.as_str());
        println!("The longest string is {}", result);
    }
}

result 的生命周期等于參數(shù)中生命周期最小的,因此要等于 string2 的生命周期,也就是說,result 要活得和 string2 一樣久。如過我們將上面的代碼改變?yōu)槿缦滤尽?/p>

fn main() {
    let string1 = String::from("long string is long");
    let result;
    {
        let string2 = String::from("xyz");
        result = longest(string1.as_str(), string2.as_str());
    }
    println!("The longest string is {}", result);
}

那么將會導(dǎo)致錯(cuò)誤,因?yàn)榫幾g器知道string2活不到最后一行打印。而string1可以活到打印,但是編譯器并不知道longest返回的是誰。
函數(shù)的返回值如果是一個(gè)引用類型,那么它的生命周期只會來源于:

  • 函數(shù)參數(shù)的生命周期
  • 函數(shù)體中某個(gè)新建引用的生命周期

若是后者情況,就是典型的懸垂引用場景:

#![allow(unused)]
fn main() {
fn longest<'a>(x: &str, y: &str) -> &'a str {
    let result = String::from("really long string");
    result.as_str()
}
}

上面的函數(shù)的返回值就和參數(shù) x,y 沒有任何關(guān)系,而是引用了函數(shù)體內(nèi)創(chuàng)建的字符串,而函數(shù)結(jié)束的時(shí)候會自動(dòng)釋放result的內(nèi)存,從而導(dǎo)致懸垂指針。這種情況,最好的辦法就是返回內(nèi)部字符串的所有權(quán),然后把字符串的所有權(quán)轉(zhuǎn)移給調(diào)用者:

fn longest<'a>(_x: &str, _y: &str) -> String {
    String::from("really long string")
}

fn main() {
   let s = longest("not", "important");
}

結(jié)構(gòu)體中的生命周期

在結(jié)構(gòu)體中使用引用,只要為結(jié)構(gòu)體中的每一個(gè)引用標(biāo)注上生命周期即可。

struct ImportantExcerpt<'a> {
    part: &'a str,
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Could not find a '.'");
    let i = ImportantExcerpt {
        part: first_sentence,
    };
}

part引用的first_sentence來自于novel,它的生命周期是main函數(shù),因此這段代碼可以正常工作。
ImportantExcerpt 結(jié)構(gòu)體中有一個(gè)引用類型的字段 part,因此需要為它標(biāo)注上生命周期。結(jié)構(gòu)體的生命周期標(biāo)注語法跟泛型參數(shù)語法很像,需要對生命周期參數(shù)進(jìn)行聲明 <'a>。該生命周期標(biāo)注說明,結(jié)構(gòu)體 ImportantExcerpt 所引用的字符串 str 必須比該結(jié)構(gòu)體活得更久。

生命周期消除

編譯器為了簡化用戶的使用,運(yùn)用了生命周期消除大法。例如:

fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

對于 first_word 函數(shù),它的返回值是一個(gè)引用類型,那么該引用只有兩種情況:

  • 從參數(shù)獲取
  • 從函數(shù)體內(nèi)部新創(chuàng)建的變量獲取

如果是后者,就會出現(xiàn)懸垂引用,最終被編譯器拒絕,因此只剩一種情況:返回值的引用是獲取自參數(shù),這就意味著參數(shù)和返回值的生命周期是一樣的。道理很簡單,我們能看出來,編譯器自然也能看出來,因此,就算我們不標(biāo)注生命周期,也不會產(chǎn)生歧義。

只不過,消除規(guī)則不是萬能的,若編譯器不能確定某件事是正確時(shí),會直接判為不正確,那么你還是需要手動(dòng)標(biāo)注生命周期
函數(shù)或者方法中,參數(shù)的生命周期被稱為 輸入生命周期,返回值的生命周期被稱為 輸出生命周期。

三條消除原則

1.每一個(gè)引用參數(shù)都會獲得獨(dú)自的生命周期

例如一個(gè)引用參數(shù)的函數(shù)就有一個(gè)生命周期標(biāo)注: fn foo<'a>(x: &'a i32),兩個(gè)引用參數(shù)的有兩個(gè)生命周期標(biāo)注:fn foo<'a, 'b>(x: &'a i32, y: &'b i32), 依此類推。

2.若只有一個(gè)輸入生命周期(函數(shù)參數(shù)中只有一個(gè)引用類型),那么該生命周期會被賦給所有的輸出生命周期,也就是所有返回值的生命周期都等于該輸入生命周期

例如函數(shù) fn foo(x: &i32) -> &i32,x 參數(shù)的生命周期會被自動(dòng)賦給返回值 &i32,因此該函數(shù)等同于 fn foo<'a>(x: &'a i32) -> &'a i32

3.若存在多個(gè)輸入生命周期,且其中一個(gè)是 &self 或 &mut self,則 &self 的生命周期被賦給所有的輸出生命周期。

擁有 &self 形式的參數(shù),說明該函數(shù)是一個(gè) 方法,該規(guī)則讓方法的使用便利度大幅提升。

讓我們假裝自己是編譯器,然后看下以下的函數(shù)該如何應(yīng)用這些規(guī)則:

例子1

fn first_word(s: &str) -> &str // 實(shí)際項(xiàng)目中的手寫代碼

首先,我們手寫的代碼如上所示時(shí),編譯器會先應(yīng)用第一條規(guī)則,為每個(gè)參數(shù)標(biāo)注一個(gè)生命周期:

fn first_word<'a>(s: &'a str) -> &str  // 編譯器自動(dòng)為參數(shù)添加生命周期

此時(shí),第二條規(guī)則就可以進(jìn)行應(yīng)用,因?yàn)楹瘮?shù)只有一個(gè)輸入生命周期,因此該生命周期會被賦予所有的輸出生命周期:

fn first_word<'a>(s: &'a str) -> &'a str  // 編譯器自動(dòng)為返回值添加生命周期

此時(shí),編譯器為函數(shù)簽名中的所有引用都自動(dòng)添加了具體的生命周期,因此編譯通過,且用戶無需手動(dòng)去標(biāo)注生命周期,只要按照 fn first_word(s: &str) -> &str的形式寫代碼即可。

例子2

fn longest(x: &str, y: &str) -> &str // 實(shí)際項(xiàng)目中的手寫代碼

首先,編譯器會應(yīng)用第一條規(guī)則,為每個(gè)參數(shù)都標(biāo)注生命周期:

fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &str 

但是此時(shí),第二條規(guī)則卻無法被使用,因?yàn)檩斎肷芷谟袃蓚€(gè),第三條規(guī)則也不符合,因?yàn)樗呛瘮?shù),不是方法,因此沒有 &self 參數(shù)。在套用所有規(guī)則后,編譯器依然無法為返回值標(biāo)注合適的生命周期,因此,編譯器就會報(bào)錯(cuò),提示我們需要手動(dòng)標(biāo)注生命周期。

例子3

impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {}", announcement);
        self.part
    }
}

首先,編譯器應(yīng)用第一規(guī)則,給予每個(gè)輸入?yún)?shù)一個(gè)生命周期。

impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part<'b>(&'a self, announcement: &'b str) -> &str {
        println!("Attention please: {}", announcement);
        self.part
    }
}

需要注意的是,編譯器不知道 announcement 的生命周期到底多長,因此它無法簡單的給予它生命周期 'a,而是重新聲明了一個(gè)全新的生命周期 'b。接著,編譯器應(yīng)用第三規(guī)則,將 &self 的生命周期賦給返回值 &str

impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part<'b>(&'a self, announcement: &'b str) -> &'a str {
        println!("Attention please: {}", announcement);
        self.part
    }
}

盡管我們沒有給方法標(biāo)注生命周期,但是在第一和第三規(guī)則的配合下,編譯器依然完美的為我們亮起了綠燈。

生命周期約束

我們來看下面這個(gè)例子。將返回值的生命周期聲明為’b,但是實(shí)際返回的是生命周期為’a的self.part。

impl<'a: 'b, 'b> ImportantExcerpt<'a> {
    fn announce_and_return_part(&'a self, announcement: &'b str) -> &'b str {
        println!("Attention please: {}", announcement);
        self.part
    }
}
  • 'a: 'b,是生命周期約束語法,跟泛型約束非常相似,用于說明 'a 必須比 'b 活得久
  • 可以把 'a 和 'b 都在同一個(gè)地方聲明(如上),或者分開聲明但通過 where 'a: 'b 約束生命周期關(guān)系,如下:
impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part<'b>(&'a self, announcement: &'b str) -> &'b str
    where
        'a: 'b,
    {
        println!("Attention please: {}", announcement);
        self.part
    }
}

加上這個(gè)約束,告訴編譯器’a活的比’b更久,引用’a不會產(chǎn)生懸垂指針(無效引用)。

靜態(tài)生命周期

rust中有一個(gè)非常特殊的生命周期,那就是’static,擁有該生命周期的引用可以活的和整個(gè)程序一樣久。實(shí)際上字符串字面值就擁有’static生命周期,它被硬編碼進(jìn)rust的二進(jìn)制文件中。'static生命周期非常強(qiáng)大,隨意使用它相當(dāng)于放棄了生命周期檢查。遇到因?yàn)樯芷趯?dǎo)致的編譯不通過問題,首先想的應(yīng)該是:是否是我們試圖創(chuàng)建一個(gè)懸垂引用,或者是試圖匹配不一致的生命周期,而不是簡單粗暴的用 'static 來解決問題。除非實(shí)在遇到解決不了的生命周期標(biāo)注問題,可以嘗試’static生命周期。例如:

fn t() -> &'static str{
    "qwert"
}

fn t() -> &'static str{
    "qwert"
}

注意,使用’static生命周期的時(shí)候,不需要提前聲明。

一個(gè)復(fù)雜例子: 泛型,特征約束以及生命周期

use std::fmt::Display;

fn longest_with_an_announcement<'a, T>(
    x: &'a str,
    y: &'a str,
    ann: T,
) -> &'a str
where
    T: Display,
{
    println!("Announcement! {}", ann);
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

例子中,包含了生命周期’a,泛型T以及對T的約束Display(因?yàn)槲覀冃枰蛴nn)。

到此這篇關(guān)于rust生命周期的文章就介紹到這了,更多相關(guān)rust生命周期內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Rust使用Channel實(shí)現(xiàn)跨線程傳遞數(shù)據(jù)

    Rust使用Channel實(shí)現(xiàn)跨線程傳遞數(shù)據(jù)

    消息傳遞是一種很流行且能保證安全并發(fā)的技術(shù),Rust也提供了一種基于消息傳遞的并發(fā)方式,在rust里使用標(biāo)準(zhǔn)庫提供的Channel來實(shí)現(xiàn),下面我們就來學(xué)習(xí)一下如何使用Channel實(shí)現(xiàn)跨線程傳遞數(shù)據(jù)吧
    2023-12-12
  • Rust生命周期常見誤區(qū)(中英對照)全面指南

    Rust生命周期常見誤區(qū)(中英對照)全面指南

    這篇文章主要WEIDJAI?介紹了Rust生命周期常見誤區(qū)(中英對照)的全面指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • 如何使用Rust直接編譯單個(gè)的Solidity合約

    如何使用Rust直接編譯單個(gè)的Solidity合約

    本文介紹了如何使用Rust語言直接編譯Solidity智能合約,特別適用于沒有外部依賴或flatten后的合約,一般情況下,Solidity開發(fā)者使用Hardhat或Foundry框架,本文給大家介紹如何使用Rust直接編譯單個(gè)的Solidity合約,感興趣的朋友一起看看吧
    2024-09-09
  • Rust Aya 框架編寫 eBPF 程序

    Rust Aya 框架編寫 eBPF 程序

    這篇文章主要介紹了Rust Aya 框架編寫 eBPF 程序方法的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • 最新Rust錯(cuò)誤處理簡介

    最新Rust錯(cuò)誤處理簡介

    Rust并不像C++一樣使用try?catch的異常機(jī)制來進(jìn)行錯(cuò)誤處理,他將錯(cuò)誤分為可恢復(fù)錯(cuò)誤和不可恢復(fù)錯(cuò)誤兩類,主要使用panic!宏和Result<T,E>類型來進(jìn)行錯(cuò)誤處理,這篇文章主要介紹了Rust錯(cuò)誤處理簡介,需要的朋友可以參考下
    2022-11-11
  • rust?zip異步壓縮與解壓的代碼詳解

    rust?zip異步壓縮與解壓的代碼詳解

    在使用actix-web框架的時(shí)候,如果使用zip解壓任務(wù)將會占用一個(gè)工作線程,因?yàn)閦ip庫是同步阻塞的,想用異步非阻塞需要用另一個(gè)庫,下面介紹下rust?zip異步壓縮與解壓的示例,感興趣的朋友一起看看吧
    2024-04-04
  • Rust之Substrate框架中Core詳解

    Rust之Substrate框架中Core詳解

    Substrate是一個(gè)用于構(gòu)建區(qū)塊鏈的開發(fā)框架,它由Parity團(tuán)隊(duì)基于Rust語言開發(fā)而成,是一個(gè)開箱即用的區(qū)塊鏈構(gòu)造器,本文詳細(xì)介紹了Substrate框架中的Core,需要的朋友可以參考下
    2023-05-05
  • 一文學(xué)會Rust語言如何操作JSON

    一文學(xué)會Rust語言如何操作JSON

    JSON在Web開發(fā)中被廣泛應(yīng)用于數(shù)據(jù)交換,本文主要介紹了Rust語言操作JSON,包括序列化、反序列化、JSON創(chuàng)建等多個(gè)方面,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • Rust實(shí)現(xiàn)grep命令行工具的方法

    Rust實(shí)現(xiàn)grep命令行工具的方法

    這篇文章主要介紹了Rust實(shí)現(xiàn)grep命令行工具的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • Rust中HashMap類型的使用詳解

    Rust中HashMap類型的使用詳解

    Rust中一種常見的集合類型是哈希映射,本文主要介紹了Rust中HashMap類型的使用詳解,包含創(chuàng)建訪問修改遍歷等,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03

最新評論