最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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)知識(shí),需要的朋友可以參考下

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的編譯器會(huì)使用借用檢查器來檢查我們程序的借用正確性。例如:

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

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

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

在編譯期,Rust 會(huì)比較兩個(gè)變量的生命周期,結(jié)果發(fā)現(xiàn) r 明明擁有生命周期 'a,但是卻引用了一個(gè)小得多的生命周期 'b,在這種情況下,編譯器會(huì)認(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編譯器會(huì)報(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ù)聲明。就會(huì)發(fā)現(xiàn)可以順利通過編譯。因此,像這樣的函數(shù),我們無法判斷它是返回x還是y,那么只好手動(dòng)進(jìn)行生命周期聲明。上面的提示就是手動(dòng)聲明聲明周期的語法。

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

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

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

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

該函數(shù)簽名表明對(duì)于某些生命周期 '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);
}

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

  • 函數(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í)候會(huì)自動(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ù)語法很像,需要對(duì)生命周期參數(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[..]
}

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

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

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

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

三條消除原則

1.每一個(gè)引用參數(shù)都會(huì)獲得獨(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è)引用類型),那么該生命周期會(huì)被賦給所有的輸出生命周期,也就是所有返回值的生命周期都等于該輸入生命周期

例如函數(shù) fn foo(x: &i32) -> &i32,x 參數(shù)的生命周期會(huì)被自動(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í),編譯器會(huì)先應(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è)輸入生命周期,因此該生命周期會(huì)被賦予所有的輸出生命周期:

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)目中的手寫代碼

首先,編譯器會(huì)應(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)注合適的生命周期,因此,編譯器就會(huì)報(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不會(huì)產(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)該是:是否是我們?cè)噲D創(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以及對(duì)T的約束Display(因?yàn)槲覀冃枰蛴nn)。

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

相關(guān)文章

  • Rust編寫自動(dòng)化測試實(shí)例權(quán)威指南

    Rust編寫自動(dòng)化測試實(shí)例權(quán)威指南

    這篇文章主要為大家介紹了Rust編寫自動(dòng)化測試實(shí)例權(quán)威指南詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • libbpf和Rust開發(fā)ebpf程序?qū)崙?zhàn)示例

    libbpf和Rust開發(fā)ebpf程序?qū)崙?zhàn)示例

    這篇文章主要為大家介紹了libbpf和Rust開發(fā)ebpf程序?qū)崙?zhàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Rust中的Cargo構(gòu)建、運(yùn)行、調(diào)試

    Rust中的Cargo構(gòu)建、運(yùn)行、調(diào)試

    Cargo是rustup安裝后自帶的,Cargo?是?Rust?的構(gòu)建系統(tǒng)和包管理器,這篇文章主要介紹了Rust之Cargo構(gòu)建、運(yùn)行、調(diào)試,需要的朋友可以參考下
    2022-09-09
  • Rust可迭代類型迭代器正確創(chuàng)建自定義可迭代類型的方法

    Rust可迭代類型迭代器正確創(chuàng)建自定義可迭代類型的方法

    在 Rust 中, 如果一個(gè)類型實(shí)現(xiàn)了 Iterator, 那么它會(huì)被同時(shí)實(shí)現(xiàn) IntoIterator, 具體邏輯是返回自身, 因?yàn)樽陨砭褪堑?這篇文章主要介紹了Rust可迭代類型迭代器正確創(chuàng)建自定義可迭代類型的方法,需要的朋友可以參考下
    2023-12-12
  • Rust中into和from用法及區(qū)別介紹

    Rust中into和from用法及區(qū)別介紹

    這篇文章主要介紹了Rust中的?into和from使用及區(qū)別介紹,into和from是Rust語言中兩個(gè)用于類型轉(zhuǎn)換的函數(shù),它們分別屬于Into和From這兩個(gè)trait,本文通過實(shí)例代碼詳細(xì)講解,需要的朋友可以參考下
    2023-04-04
  • 解析Rust?struct?中的生命周期

    解析Rust?struct?中的生命周期

    rust?的生命周期保證了內(nèi)存的安全性,同時(shí)也增加了開發(fā)者的心智負(fù)擔(dān)。是在上線之前多費(fèi)心思寫代碼,還是在上線以后忙忙活活查問題,這是個(gè)?trade?off?問題,這篇文章主要介紹了Rust?struct?中的生命周期,需要的朋友可以參考下
    2022-10-10
  • 通過rust實(shí)現(xiàn)自己的web登錄圖片驗(yàn)證碼功能

    通過rust實(shí)現(xiàn)自己的web登錄圖片驗(yàn)證碼功能

    本文介紹了如何使用Rust和imagecrate庫生成圖像驗(yàn)證碼,首先,通過Cargo.toml文件添加image依賴,然后,生成純色圖片并編輯驗(yàn)證圖片,接著,編寫隨機(jī)函數(shù)獲取字符,并通過循環(huán)生成驗(yàn)證碼圖片,最終,通過運(yùn)行函數(shù)驗(yàn)證驗(yàn)證碼圖片是否生成,感興趣的朋友一起看看吧
    2025-03-03
  • Rust應(yīng)用調(diào)用C語言動(dòng)態(tài)庫的操作方法

    Rust應(yīng)用調(diào)用C語言動(dòng)態(tài)庫的操作方法

    這篇文章主要介紹了Rust應(yīng)用調(diào)用C語言動(dòng)態(tài)庫,本文記錄了筆者編寫一個(gè)簡單的C語言動(dòng)態(tài)庫,并通過Rust調(diào)用動(dòng)態(tài)庫導(dǎo)出的函數(shù),需要的朋友可以參考下
    2023-01-01
  • Rust語言實(shí)現(xiàn)圖像編碼轉(zhuǎn)換

    Rust語言實(shí)現(xiàn)圖像編碼轉(zhuǎn)換

    image-rs庫是?Rust?社區(qū)中廣泛使用的一個(gè)開源庫,它提供了豐富的圖像編解碼功能,本文主要介紹了Rust語言實(shí)現(xiàn)圖像編碼轉(zhuǎn)換,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-05-05
  • Rust處理錯(cuò)誤的實(shí)現(xiàn)方法

    Rust處理錯(cuò)誤的實(shí)現(xiàn)方法

    程序在運(yùn)行的過程中,總是會(huì)不可避免地產(chǎn)生錯(cuò)誤,而如何優(yōu)雅地解決錯(cuò)誤,也是語言的設(shè)計(jì)哲學(xué)之一。本文就來和大家來了Rust是如何處理錯(cuò)誤的,感興趣的可以了解一下
    2023-03-03

最新評(píng)論

将乐县| 宁明县| 克拉玛依市| 渝北区| 随州市| 观塘区| 汾西县| 界首市| 咸宁市| 毕节市| 蕲春县| 绥化市| 滁州市| 宜黄县| 五寨县| 开原市| 拜泉县| 镇平县| 连江县| 陵川县| 石棉县| 方正县| 特克斯县| 云南省| 万荣县| 格尔木市| 丘北县| 玉林市| 卢氏县| 宾川县| 沧州市| 太仓市| 泸溪县| 大同县| 东阿县| 苗栗市| 仁寿县| 扎赉特旗| 民乐县| 青田县| 吉首市|