Rust 基礎(chǔ)知識(shí)17 - 生命周期

簡(jiǎn)介

  • 接上回書
  • 其實(shí)Rust 的生命周期并不難理解,要知道它為什么使用標(biāo)注聲明周期的方式就不難理解了。
  • 注意不要死記硬背,沒有用,要去理解就會(huì)豁然開朗。

生命周期的基礎(chǔ)

  • Rust 的每個(gè)引用都有自己的生命周期。
  • 聲明周期,引用保持有效的作用域。
  • 大多數(shù)情況下,Rust的生命周期是隱士的、可以被推斷的。
  • 當(dāng)引用的生命周期可能以不同的方式相互關(guān)聯(lián)時(shí),需要手動(dòng)標(biāo)注聲明周期。
  • 生命周期的類型分為,輸入生命周期和輸出生命周期兩種。
  • 'staitc 是一個(gè)比較特殊的聲明周期標(biāo)注,不要亂用,稍后了解。
  • 生命周期約束的主要目標(biāo)是避免出現(xiàn)dangling reference的情況。
  • 生命周期超出作用域的小例子:
fn main() {
    let mut x;
    {
        let y = String::from("hello");
        // x = y; // 這樣就可以,因?yàn)檫@樣相當(dāng)于借用作用域
        x = &y; // 這樣就不行,顯然y的引用作用域小于x
    }
    println!("Str:{}", x);
  • 編譯后報(bào)錯(cuò):(這個(gè)很好理解)


    image.png

借用檢查器

  • Rust 編譯器的借用檢查器,用來判斷借用是否合法。
  • 舉個(gè)簡(jiǎn)單的例子:
// 你會(huì)發(fā)現(xiàn)如果返回的是一個(gè)引用地址,無論你函數(shù)寫的多簡(jiǎn)單,都會(huì)報(bào)錯(cuò) `missing lifetime specifier.`
fn longest(x:&str, y:&str) -> &str {
    if x.len() > y.len() {
        x
    }else{
        y
    }
}
  • 先想想為什么會(huì)這樣,假定使用 loginest 的函數(shù)是這樣的:
fn main() {
    // 存放結(jié)果值的變量
    let long_str; 
    // 創(chuàng)建兩個(gè)生命周期不同的
    let x = "abc".to_string();
    {
        // y 的作用域明顯比x要短
        let y = "bbccd".to_string();
        // 獲取x和y中相較而言比較長的字符串。
        long_str = longest(x.as_str(), y.as_str());
    }
    // 這是后打印會(huì)出現(xiàn)什么情況,如果 long_str = x 那么程序應(yīng)該OK,但是如果 =y 則應(yīng)該超出作用域
    println!("Longest str: {}", long_str);
}

  • 理論上,如果 long_str = x 那么程序應(yīng)該OK,但是如果 =y 則應(yīng)該超出作用域,Rust 的強(qiáng)大編譯器當(dāng)然不會(huì)允許這種空指針的情況出現(xiàn)。
  • 所以怎么辦呢,需要在泛型中添加'作用的標(biāo)記,舉例:
// 加上 <'a> 同時(shí)  x:&'a str, y:&'a str 表示使用兩個(gè)生命周期中較短的那個(gè)作為返回值->'a str 
fn longest<'a>(x:&'a str, y:&'a str) -> &'a str {
    if x.len() > y.len() {
        x
    }else{
        y
    }
}
image.png
  • OK這是后可以看到,代碼分析工具都知道 y 的聲明周期長度不夠了,上面的定義就是說作為返回值的&str 生命周期應(yīng)該是 x,y 者中最短的。

深入理解生命周期

  • 指定生命周期參數(shù)的方式依賴于函數(shù)所做的事情
  • 從函數(shù)返回引用時(shí),返回類型的生命周期參數(shù)需要與其中一個(gè)參數(shù)的聲明周期匹配。
  • 如果返回的引用沒有指向任何參數(shù),那么它只能引用函數(shù)內(nèi)創(chuàng)建的值,此時(shí)就會(huì)出現(xiàn)懸垂引用,該值在函數(shù)結(jié)束時(shí)就走出了作用域。
  • 不講究代碼意義,如果不修改main函數(shù)如何讓程序正常編譯呢?(主要是增強(qiáng)立即,其實(shí)這么修改后函數(shù)變得沒有意義了)
// 返回值使用的聲明周期是 'a 'a 符合println!() 宏的作用域范圍所以編譯成功
fn longest<'a, 'b>(x:&'a str, y:&'b str) -> &'a str {
    x
}
  • 上面的代碼就可以通過編譯,我想通過上面的代碼已經(jīng)可以更深入的了解Rust的聲明周期了。

Struct 中定義的聲明周期標(biāo)注

  • 聲明周期標(biāo)注不僅僅可以應(yīng)用在函數(shù)中,struct 定義中也可以,首先我們看一下Struct 中可以包括哪些內(nèi)容:
1、自持類型。
2、引用(需要在每個(gè)引用上添加生命周期標(biāo)注)

實(shí)例如下:

fn main() {
    let info = String::from("File not found.");
    // 存放結(jié)果值的變量
    let exc = ImportantExcepiton {
        part: info.as_str()
    };

    println!("{:?}", exc);
}
#[derive(Debug)]
struct ImportantExcepiton<'a> {
    part: &'a str,
}
  • 上面的代碼中如果你的結(jié)構(gòu)中定義了引用類型,但是卻沒有生命期作用域就會(huì)出問題,可以自己多試試。

聲明周期的省略

  • 生命周期在實(shí)際使用中,有些情況已經(jīng)很明顯生命周期范圍,這時(shí)就可以進(jìn)行省略,基礎(chǔ)原則如下:
1、每個(gè)引用類型的參數(shù)都有自己的生命周期。
2、如果只有一個(gè)輸入生命周期參數(shù),那么該生命周期被賦給所有的輸出生命周期參數(shù)。
3、如果有多個(gè)輸入生命周期,但其中一個(gè)是&self或&mut self(方法),那么self的生命周期會(huì)被賦給所有的輸出生命周期參數(shù),(也就是結(jié)構(gòu)方法)
  • 單參數(shù)推導(dǎo)流程假想:
1、fn first_word(s: &str) -> &str ; 編譯器向下推斷
2、fn first_word<'a>(s: &'a str)->&str ; 編譯器繼續(xù)向下推斷
3、fn first_word<'a>(s: &'a str)-> &'a str; 全部推斷成功,所以可以簡(jiǎn)寫。
  • 多參數(shù)推導(dǎo)流程假想:(多參數(shù)無法推導(dǎo))
1、fn longest(x:&str, y:&str) -> &str; 編譯器向下推導(dǎo)
2、fn longest<'a,'b>(x:&'a str, y:&'b str) -> &str; 出現(xiàn)二義性,無法繼續(xù)推導(dǎo)報(bào)錯(cuò)!

struct 方法定義中的生命周期標(biāo)注

  • 在哪兒聲明和使用生命周期參數(shù),依賴于,生命周期參數(shù)是否和字段、方法的參數(shù)或返回值有關(guān)。
  • 聲明方法,在impl 后聲明,在struct 名后使用,這些生命周期是 struct 類型的一部分。
  • 說的那么負(fù)責(zé)就是把聲明周期定義引入進(jìn)來了而已,所以做方法實(shí)現(xiàn)的時(shí)候也需要引用這個(gè)定義。

fn main() {
    let info = String::from("File not found.");
    // 存放結(jié)果值的變量
    let exc = ImportantExcepiton {
        part: info.as_str()
    };

    println!("{:?}", exc);
}
#[derive(Debug)]
struct ImportantExcepiton <'a>{
    part: &'a str,
}

// 前面是聲明,后面的是使用
impl<'a> ImportantExcepiton <'a> {
    // 返回值這里被省略掉 'a 這是遵循省略原則的,仔細(xì)閱讀上面的內(nèi)容
    fn callname(&self ) -> &str{
        self.part
    }
}

靜態(tài)聲明周期 'static

  • 'static 是一個(gè)特殊的生命周期,占用整個(gè)程序的持續(xù)時(shí)間,例如所有的字符串字面值都擁有'static 生命周期,例如 let s:&'static str = "I have a static lifetime."。
  • 使用'static 前一定要三思。

結(jié)束

  • 感謝閱讀
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容