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

深入了解Rust的生命周期

 更新時(shí)間:2022年11月16日 09:18:24   作者:古明地覺  
生命周期指的是引用保持有效的作用域,Rust?的每個(gè)引用都有自己的生命周期。本文將通過示例和大家詳細(xì)說說Rust的生命周期,需要的可以參考一下

楔子

Rust 的每個(gè)引用都有自己的生命周期,生命周期指的是引用保持有效的作用域。大多數(shù)情況下,引用是隱式的、可以被推斷出來的,但當(dāng)引用可能以不同的方式互相關(guān)聯(lián)時(shí),則需要手動標(biāo)注生命周期。

fn?main()?{
????let?r;
????{
????????let?x?=?5;
????????r?=?&x;
????}??//?此處?r?不再有效
????println!("{}",?r);
}

執(zhí)行的時(shí)候會報(bào)出如下錯(cuò)誤:borrowed value does not live long enough,意思就是借用的值存活的時(shí)間不夠長。因?yàn)榘?x 的引用給 r 之后,x 就被銷毀了,那么 r 就成為了一個(gè)懸空引用。

而 Rust 會通過借用檢查器,來檢查借用是否合法,顯然上述代碼在執(zhí)行打印語句的時(shí)候,r 已經(jīng)不合法了。

fn?longest(x:?&str,?y:?&str)?->?&str?{
????if?x.len()?>?y.len()?{
????????x
????}?else?{
????????y?
????}
}

這段代碼也是不合法的,原因就是返回值要么是 x 要么是 y,但具體是哪一個(gè)不知道,并且它們的生命周期也都不知道。所以無法通過比較作用域,來判斷返回的引用是否是一致有效的,而借用檢查器也是做不到的,原因就是它不知道返回值的生命周期是跟 x 有關(guān)系還是跟 y 有關(guān)系。事實(shí)上,這個(gè)跟函數(shù)體的邏輯也沒有關(guān)系,函數(shù)的聲明就決定了它做不到這一點(diǎn)。

因此我們需要引入生命周期。

生命周期標(biāo)注語法

首先生命周期標(biāo)注并不會改變引用的生命長度,當(dāng)指定了生命周期參數(shù),函數(shù)可以接收帶有任何生命周期的引用。生命周期的標(biāo)注:描述了多個(gè)引用的生命周期間的關(guān)系,但不影響生命周期本身。

現(xiàn)在光讀起來可能有點(diǎn)繞,別急,一會兒會解釋。

生命周期參數(shù)名以 ' 開頭,并且名字非常短,通常為 a;標(biāo)注位置在 & 后面,只有 & 才需要生命周期。因?yàn)槟阋昧艘粋€(gè)值,那么這個(gè)值的存活時(shí)間需要知道,不然人家都被銷毀了還傻傻地用。

  • &i32:一個(gè)引用;
  • &'a i32:帶有顯式生命周期的引用;
  • &'a mut i32:帶有顯式生命周期的可變引用;

其實(shí)單個(gè)生命周期標(biāo)注本身沒有什么意義,它是為了向 Rust 描述多個(gè)具有生命周期的參數(shù)之間的關(guān)系。并且生命周期和泛型一樣,也要聲明在尖括號內(nèi)。

//?簽名里面的生命周期必須要有
//?相當(dāng)于告訴?Rust?有這么一個(gè)生命周期?'a
fn?longest<'a>(x:?&'a?str,?y:?&'a?str)?->?&'a?str?{
????if?x.len()?>?y.len()?{
????????x
????}?else?{
????????y
????}
}

此時(shí)代碼是合法的,但是注意:我們并沒有改變傳入的值和返回的值的生命周期,我們只是向借用檢查器指出了一些用于檢查非法調(diào)用的一些約束而已,而借用檢查器并不需要知道 x、y 的具體存活時(shí)長。

而事實(shí)上如果函數(shù)引用外部的變量,那么單靠 Rust 確定函數(shù)和返回值的生命周期幾乎是不可能的事情。因?yàn)楹瘮?shù)傳遞什么參數(shù)都是我們決定的,這樣的話函數(shù)在每次調(diào)用時(shí)使用的生命周期都可能發(fā)生變化,正因如此我們才需要手動對生命周期進(jìn)行標(biāo)注。

//?準(zhǔn)確來說?'a?指的就是?x?和?y?生命周期重疊的那一部分
//?而返回值的生命周期不能超重疊的部分
fn?longest<'a>(x:?&'a?str,?y:?&'a?str)?->?&'a?str?{
????if?x.len()?>?y.len()?{
????????x
????}?else?{
????????y
????}
}

fn?main()?{
????let?x?=?String::from("hello");
????{
????????let?y?=?String::from("satori");
????????let?result?=?longest(&x,?&y);
????????println!("result?=?{}",?result);
????????//?result?=?satori
????}
}

目前是沒有問題的,因?yàn)?x 和 y 的生命周期重疊的部分是 y,然后返回值 result 和 y 也是一樣的。但如果我們把代碼改一下,將 println! 語句移到花括號外面:

fn?longest<'a>(x:?&'a?str,?y:?&'a?str)?->?&'a?str?{
????if?x.len()?>?y.len()?{
????????x
????}?else?{
????????y
????}
}

fn?main()?{
????let?x?=?"hello".to_string();
????let?result;
????{
????????let?y?=?"satori".to_string();
????????result?=?longest(&x,?&y);
????}
????println!("result?=?{}",?result);??

此時(shí)就報(bào)錯(cuò)了:borrowed value does not live long enough。相信你已經(jīng)猜到了,因?yàn)?x、y 生命周期重疊的部分是 y,返回值 result 的生命周期不能超過它。但當(dāng)前明顯超過了,所以報(bào)錯(cuò)。

所以說生命周期標(biāo)注對變量沒有什么影響,它只是給了借用檢查器一個(gè)可以用來判斷的約束罷了。

總結(jié)一下就是:生命周期用來關(guān)聯(lián)函數(shù)參數(shù)和返回值之間的聯(lián)系,一旦它們?nèi)〉昧四撤N聯(lián)系,那么 Rust 就獲得了足夠多的信息來保證內(nèi)存安全的操作,并且阻止那些出現(xiàn)懸空指針或者其它導(dǎo)致內(nèi)存安全的行為。

到目前為止,你也許還不太了解生命周期,別著急,我們繼續(xù)往下看。

結(jié)構(gòu)體中的生命周期標(biāo)注

struct 里面可以放任意類型,但是不能放引用,比如下面的結(jié)構(gòu)體定義就是錯(cuò)誤的。

struct?Girl?{
????name:?&str,
????age:?i32
}

結(jié)構(gòu)體如果是合法的,那么它內(nèi)部的所有成員值都要是合法的。但現(xiàn)在 name 是一個(gè)引用,所以結(jié)構(gòu)體實(shí)例化的時(shí)候一定會引用某個(gè)字符串,這就使得字符串存活是結(jié)構(gòu)體實(shí)例存活的前提。

但在實(shí)際編碼中,這兩者的存活時(shí)間沒有什么關(guān)系,有可能你在使用結(jié)構(gòu)體實(shí)例訪問 name 成員的時(shí)候,它引用的字符串都已經(jīng)被銷毀了。所以 Rust 不允許我們這么做,我們之前是將 name 的類型指定為 String,也就是讓結(jié)構(gòu)體持有全部數(shù)據(jù)的所有權(quán)。 

而如果非要將類型指定為引用的話,那么必須指定生命周期。

//?實(shí)例.name?會引用外部的一個(gè)字符串,所以要指定生命周期
//?表示字符串的存活時(shí)間一定比結(jié)構(gòu)體實(shí)例要長
//?否則字符串沒了,而實(shí)例還在,那么就會出現(xiàn)懸空引用
#[derive(Debug)]
struct?Girl<'a>?{
????name:?&'a?str,
????age:?i32
}

fn?main()?{
????let?g;
????{
????????let?name?=?String::from("古明地覺");
????????g?=?Girl{name:?&name,?age:?16};
????}
????println!("{:?}",?g);
}

因?yàn)橹付松芷冢诰幾g的時(shí)候借用檢查器就可以檢測出存活時(shí)間是否合法。首先 g 的存活時(shí)間是整個(gè) main 函數(shù),而 name 的存活時(shí)間是內(nèi)部的花括號那一段作用域,比 g 的存活時(shí)間短,因此編譯出錯(cuò)。

所以通過生命周期標(biāo)注,Rust 在編譯期間就能通過借用檢查器檢測出引用是否合法,Rust 不會將這種錯(cuò)誤留到運(yùn)行時(shí)。

生命周期的省略

當(dāng)一個(gè)函數(shù)返回了一個(gè)引用時(shí),往往需要指定生命周期,而它的目的就是為了保證返回的引用是合法的。如果不合法,在編譯階段就能找出來。

fn?f(s:?&str)?->?&str?{
????s
}

函數(shù)參數(shù)出現(xiàn)了引用,返回值也有引用,應(yīng)該指定生命周期呀。是的,在早期版本這段代碼是編譯不過的,它需要你這么寫:

fn?f<'a>(s:?&'a?str)?->?&'a?str?{
????"xxx"
}

但是久而久之,Rust 團(tuán)隊(duì)發(fā)現(xiàn)對于這種場景實(shí)在沒有必要一遍又一遍的重復(fù)編寫生命周期,并且這種只有一個(gè)參數(shù)完全是可以預(yù)測的,有明確的模式。于是 Rust 團(tuán)隊(duì)就將這些模式寫入了借用檢查器,可以自動進(jìn)行推導(dǎo),而無需顯式地寫上生命周期標(biāo)注。

所以在 Rust 引用分析中編入的模式被稱為生命周期省略規(guī)則:

  • 這些規(guī)則無需開發(fā)者來遵守;
  • 對于一些特殊情況,由編譯器來考慮;
  • 如果你的代碼符合這些規(guī)則,就無需顯式標(biāo)注生命周期;

如果生命周期在函數(shù)/方法的參數(shù)中,則被稱為輸入生命周期;在函數(shù)/方法的返回值中,則被稱為輸出生命周期。而 Rust 要能夠在編譯期間基于輸入生命周期,來確定輸出生命周期,如果能夠確定,那么便是合法的。

而當(dāng)我們省略生命周期時(shí),Rust 就會基于內(nèi)置的省略規(guī)則進(jìn)行推斷,如果推斷完成后發(fā)現(xiàn)引用之間的關(guān)系還是模糊不清,就會出現(xiàn)編譯錯(cuò)誤。而解決辦法就需要我們手動標(biāo)注生命周期了,表明引用之間的相互關(guān)系。

那么 Rust 省略規(guī)則到底是怎樣的呢?

  • 規(guī)則一:每個(gè)引用類型的參數(shù)都有獨(dú)自的生命周期;
  • 規(guī)則二:如果只有一個(gè)參數(shù)具有生命周期,或者說只有一個(gè)輸入生命周期,那么該生命周期會賦值給所有的輸出生命周期;
  • 規(guī)則三:如果有多個(gè)輸入生命周期,但其中一個(gè)是 &self 或 &mut self,那么 self 的生命周期會賦值給所有的輸出生命周期;

如果編譯器在應(yīng)用完上述三個(gè)規(guī)則后,能夠計(jì)算出返回值的生命周期,則可以省略,否則不能省略。這些規(guī)則同樣適用于 fn 定義和 impl 塊,我們來舉幾個(gè)例子,感受一下整個(gè)過程。

//?函數(shù)如下,然后開始應(yīng)用三個(gè)規(guī)則
fn?first_word(s:?&str)?->?&str{};

//?1.?每個(gè)引用類型的參數(shù)都有自己的生命周期,滿足
//????所以函數(shù)相當(dāng)于變成如下
fn?first_word<'a>(s:?&'a?str)?->?&str{};

//?2.?只有一個(gè)輸入生命周期,該生命周期被賦給所有的輸出生命周期
//????顯然也是滿足的,所以函數(shù)變成如下
fn?first_word<'a>(s:?&'a?str)?->?&'a?str{};

//?3.?不滿足,所以無事發(fā)生

應(yīng)用完三個(gè)規(guī)則之后,計(jì)算出了返回值的生命周期,所以合法。

再舉個(gè)例子:

//?函數(shù)如下,然后開始應(yīng)用三個(gè)規(guī)則
fn?first_word(s1:?&str,?s2:?&str)?->?&str{};

//?1.?每個(gè)引用類型的參數(shù)都有自己的生命周期
//????顯然滿足,所以函數(shù)變成如下
fn?first_word<'a,?'b>(s1:?&'a?str,?s2:?&'b?str)?->?&str{};

//?2.?只有一個(gè)輸入生命周期,該生命周期被賦予所有的輸出生命周期
//?但是這里有兩個(gè),所以不滿足

//?3.?不滿足

當(dāng)編譯器使用了 3 個(gè)規(guī)則之后仍然無法計(jì)算出返回值的生命周期時(shí),就會出現(xiàn)編譯錯(cuò)誤,顯然上面代碼是會報(bào)錯(cuò)的。我們需要手動標(biāo)注生命周期:

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

從表面上來看 x、y 的生命周期是相同的,都是 'a,但準(zhǔn)確來說它表示的是 x、y 生命周期重疊的部分。而返回值的生命周期標(biāo)注也是 'a,所以此處的含義就表示輸出生命周期是兩個(gè)輸入生命周期重疊的部分。

longest 函數(shù)這么改的話,是合法的。

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

然后是在方法中標(biāo)注生命周期,它的語法和泛型是相似的。

//?聲明周期的語法類似于泛型
//?必須要先通過?<'a>?進(jìn)行聲明,然后才能使用
struct?Girl?<'a>?{
????name:?&'a?str,
}

//?在學(xué)習(xí)泛型的時(shí)候我們知道
//?這種方式表示為某個(gè)類型實(shí)現(xiàn)方法
//?現(xiàn)在則變成生命周期,并且?<'a>?不可以省略
impl?<'a>?Girl?<'a>?{
????fn?say_hi(&self)?->?String?{
????????String::from("hello?world")
????}

????//?此處無需指定生命周期,因?yàn)?Rust?可以推斷出來
????//?會自動將?self?的生命周期賦值給所有的輸出生命周期
????fn?get_name(&self,?useless_arg:?&str)?->?&str?{
????????self.name
????}
}
fn?main()?{
????let?name?=?String::from("古明地覺");
????let?g?=?Girl{name:&name};

????println!("{}",?g.say_hi());??//?hello?world
????println!("{}",?g.get_name(""))??//?古明地覺
}

比較簡單,另外程序中還有一個(gè)特殊的生命周期叫 'static,它表示整個(gè)程序的持續(xù)時(shí)間。所有的字符串字面量都擁有 'static 生命周期:

fn?main()?{
????let?s:?&'static?str?=?"hello";
}

為引用指定 'static 之前需要三思,是否需要引用在整個(gè)程序的生命周期內(nèi)都存活。

同時(shí)指定生命周期和泛型

生命周期的指定方式和泛型是一樣的,那如果想同時(shí)指定生命周期和泛型,應(yīng)該怎么做呢?

fn?largest<'a,?T>(x:?&'a?str,?y:?&'a?str,
??????????????????useless_arg:?T)?->?&'a?str?{
????if?x?>?y?{
????????x
????}?else?{
????????y
????}
}

fn?main()?{
????let?s1?=?"hello";
????let?s2?=?"hellO";
????println!("{}",?largest(s1,?s2,?""));
????//?hello
}

非常簡單,但要保證生命周期在前,泛型在后。

以上就是 Rust 的生命周期,它并沒有改變 Rust 變量的存活時(shí)間,只是給了借用檢查器更多的余地去推斷引用是否合法。

就目前來說,我們介紹的內(nèi)容都還很基礎(chǔ),應(yīng)該很好理解。等把基礎(chǔ)說完了,后面會介紹更多關(guān)于 Rust 的細(xì)節(jié)。最后的最后,我們再一起用 Rust 手寫一個(gè)簡易版的 Redis,并和現(xiàn)有的 Redis 做一下性能對比。

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

相關(guān)文章

  • 從零開始使用Rust編寫nginx(TLS證書快過期了)

    從零開始使用Rust編寫nginx(TLS證書快過期了)

    wmproxy已用Rust實(shí)現(xiàn)http/https代理,?socks5代理,?反向代理,?負(fù)載均衡,?靜態(tài)文件服務(wù)器,websocket代理,四層TCP/UDP轉(zhuǎn)發(fā),內(nèi)網(wǎng)穿透等,本文給大家介紹從零開始使用Rust編寫nginx(TLS證書快過期了),感興趣的朋友一起看看吧
    2024-03-03
  • 關(guān)于使用rust調(diào)用c++靜態(tài)庫并編譯nodejs包的問題

    關(guān)于使用rust調(diào)用c++靜態(tài)庫并編譯nodejs包的問題

    這篇文章主要介紹了使用rust調(diào)用c++靜態(tài)庫并編譯nodejs包的問題,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • Rust指南枚舉類與模式匹配詳解

    Rust指南枚舉類與模式匹配詳解

    這篇文章主要介紹了Rust指南枚舉類與模式匹配精講,枚舉允許我們列舉所有可能的值來定義一個(gè)類型,枚舉中的值也叫變體,今天通過一個(gè)例子給大家詳細(xì)講解,需要的朋友可以參考下
    2022-09-09
  • Rust字符串匹配Rabin-Karp算法詳解

    Rust字符串匹配Rabin-Karp算法詳解

    Rabin-Karp算法也可以叫 Karp-Rabin 算法,它是用來解決多模式串匹配問題的,它的實(shí)現(xiàn)方式有點(diǎn)與眾不同,首先是計(jì)算兩個(gè)字符串的哈希值,然后通過比較這兩個(gè)哈希值的大小來判斷是否出現(xiàn)匹配,本文詳細(xì)介紹了字符串匹配Rabin-Karp算法,需要的朋友可以參考下
    2023-05-05
  • Rust調(diào)用函數(shù)操作符?.?和?::?的區(qū)別詳解

    Rust調(diào)用函數(shù)操作符?.?和?::?的區(qū)別詳解

    在Rust中,.和::操作符都可以用來調(diào)用方法,但它們的用法有所不同,所以本文就將詳細(xì)的給大家介紹一下.和::操作符的區(qū)別,感興趣的同學(xué)跟著小編一起來學(xué)習(xí)吧
    2023-07-07
  • Rust調(diào)用C程序的實(shí)現(xiàn)步驟

    Rust調(diào)用C程序的實(shí)現(xiàn)步驟

    本文主要介紹了Rust調(diào)用C程序的實(shí)現(xiàn)步驟,包括創(chuàng)建C函數(shù)、編譯C代碼、鏈接Rust和C代碼等步驟,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • Rust中的不安全代碼詳解

    Rust中的不安全代碼詳解

    這篇文章主要為大家介紹了Rust中的不安全代碼詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • Rust?中?Deref?Coercion講解

    Rust?中?Deref?Coercion講解

    Rust 的設(shè)計(jì)理念一向是顯式比隱式好,也就是說所有的行為盡量在代碼中表現(xiàn)出來,這篇文章主要介紹了Rust?中?Deref?Coercion?介紹,需要的朋友可以參考下
    2022-10-10
  • Rust 數(shù)據(jù)分析利器polars用法詳解

    Rust 數(shù)據(jù)分析利器polars用法詳解

    這篇文章主要介紹了Rust 數(shù)據(jù)分析利器polars用法詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-08-08
  • 為什么要使用 Rust 語言、Rust 語言有什么優(yōu)勢

    為什么要使用 Rust 語言、Rust 語言有什么優(yōu)勢

    雖然 Rust 是一種通用的多范式語言,但它的目標(biāo)是 C 和 C++占主導(dǎo)地位的系統(tǒng)編程領(lǐng)域,很多朋友會問rust語言難學(xué)嗎?rust語言可以做什么,今天帶著這些疑問通過本文詳細(xì)介紹下,感興趣的朋友一起看看吧
    2022-10-10

最新評論