玩弄高耸白嫩的乳峰a片,免费国产又色又爽又黄的网站,jrs直播(无插件)直播,无遮挡国产高潮视频免费观看

rust 集合、錯(cuò)誤處理、泛型、Trait、生命周期、包

2023-06-23 15:01:33 來(lái)源:博客園 分享到:

集合組織特性相同的數(shù)據(jù);泛型可以定義任何抽象數(shù)據(jù)類型;生命周期限制所有權(quán)的作用域范圍;錯(cuò)誤處理使程序更健壯。

集合

一組特性相同的數(shù)據(jù)集合,除了基本數(shù)據(jù)類型的元組、數(shù)組。rust 標(biāo)準(zhǔn)庫(kù)提供了一些非常有用的數(shù)據(jù)結(jié)構(gòu)。


(相關(guān)資料圖)

Vector存儲(chǔ)列表

通過(guò)類型Vec定義。只能存儲(chǔ)相同類型的值,在內(nèi)存中彼此相鄰排列存儲(chǔ)。

let v:Vec = Vec::new();

通過(guò)Vec::new()創(chuàng)建一個(gè)類型實(shí)例。因?yàn)闆](méi)有初始化任何類型數(shù)據(jù),就必須指定數(shù)據(jù)類型。定義集合實(shí)例就只允許存儲(chǔ)指定的類型數(shù)據(jù)。

另一種方便創(chuàng)建集合實(shí)例的方式通過(guò) rust 提供的vec!

let v = vec![3,5,6];

定義了實(shí)例v,可以初始化數(shù)據(jù),rust 會(huì)推導(dǎo)出數(shù)據(jù)的類型。示例中默認(rèn)推導(dǎo)出類型是 i32

可以通過(guò)內(nèi)部方法,操作實(shí)例來(lái)添加、修改里面的數(shù)據(jù)

  • v.push(val)添加值。

  • v.get(index)獲取值。會(huì)得到一個(gè)可用于match匹配的Option<&T>

    也可以使用索引取值&v[1]。使用索引取值,如果超出最大索引,會(huì)報(bào)錯(cuò);使用get()方法會(huì)返回None

  • v.insert(index,val)向指定 index 位置插入數(shù)據(jù)。

  • v.remove(index)移除指定 index 位置的數(shù)據(jù),并返回該數(shù)據(jù)。

  • v.pop()移除最后一個(gè)元素,并返回。

  • v.clear()清空實(shí)例。移除所有元素。

  • v.len()返回當(dāng)前數(shù)據(jù)個(gè)數(shù)。

要可編輯實(shí)例,聲明必須使用mut可變。

let mut v:Vec = vec![];// 更新值v.push(23);v.push(4);v.push(15);v.push(56);//  取值v.get(2); // 4v[2]; // 4

在操作vec時(shí),注意引用所有權(quán)的轉(zhuǎn)義。最好的方式就是只是值借用&v

通過(guò)for循環(huán)來(lái)遍歷 vector 中的值。

for i in &v{    println!("{i}");}

在遍歷時(shí)實(shí)例v不能插入、刪除項(xiàng)。如果需要想遍歷修改每一項(xiàng)值,可以傳遞可變引用

for i in &mut v {    *i += 5;    println!("{i}");}

因?yàn)槭菍?duì)值做操作。通過(guò)*解引用取到指針指向的值。再次從實(shí)例v取值時(shí),都是最新計(jì)算過(guò)的值。

通過(guò)枚舉存儲(chǔ)多種類型

因?yàn)?vector 只能存儲(chǔ)相同類型的值。實(shí)際開(kāi)發(fā)中如果需要存儲(chǔ)不同類型的值,可以使用枚舉定義。

這樣對(duì)于 vector 而言,它都是同一種枚舉類型。

enum Color{    Red(String),    Green(u32,u32,u32),    Green(u32,u32,u32,u8)}fn main(){    let colors = vec![Color::Red(String::from("red")), Color::Green(0, 255, 0)];    for i in &colors {        println!("{:?}", i);    }}

字符串

之前已經(jīng)通過(guò)String::from()來(lái)創(chuàng)建一個(gè)字符串變量值。字符串是字節(jié)的集合。

在 rust 中只有一種字符串類型:字符串 slice str;通常是以借用的方式&str。

作為一個(gè)集合,也可以通過(guò) new 操作符創(chuàng)建一個(gè)實(shí)例。

let mut str = String::new();

但是通過(guò) new 創(chuàng)建是的實(shí)例不能初始化數(shù)據(jù)值。所以之前一直使用String::from()

也可以用一個(gè)字符串字面值創(chuàng)建 String

let s = "hboot";let str = s.to_string();

字符串是utf-8編碼的。可以包含任何可以正確編碼的數(shù)據(jù)。

操作字符串,作為一個(gè)集合,也有許多更新的方法:

  • push_str尾部附加字符串。不會(huì)獲得變量的所有權(quán),內(nèi)部采用字符串 slice。
  • push尾部附加字符。
let mut s = String::from("hboot");s.push_str(" hello");s.push("A");

也可以通過(guò)+運(yùn)算符拼接字符串,運(yùn)算位加值將會(huì)轉(zhuǎn)義所有權(quán),而被加值則必須引用

let s1 = String::from("hboot");let s2 = String::from("hello");let s = s1+&s2; // s1的所有權(quán)沒(méi)有了,s2的所有權(quán)仍然存在

也就是只能是&strString相加。不能兩個(gè)String相加,它們類型不同,確定相加是因?yàn)?rust 內(nèi)部把 String 強(qiáng)制轉(zhuǎn)換為&str

當(dāng)拼接值過(guò)多時(shí),我們可以通過(guò)format!宏來(lái)處理。它不會(huì)獲取任何字符串的所有權(quán)

let s1 = String::from("hboot");let s2 = String::from(" hello");let s3 = String::from(" world");let s = format!("{s1}{s2}{s3}");

rust 的字符串不支持索引。

  • 對(duì)于字符串值在內(nèi)存中是以字符編碼 code 存儲(chǔ)的,而不是字符。通過(guò)下標(biāo)獲取到并不是想看到的值。
  • 訪問(wèn)效率不高。通常索引預(yù)期復(fù)雜度(O(1)),但是在 rust 中,需要需要從頭開(kāi)始到索引位置遍歷字符是否有效。

所以遍歷字符串最好的方式明確需要的是字符還是字節(jié)。字符通過(guò)chars方法將其分開(kāi)并返回多個(gè)char類型的值;字節(jié)則使用bytes方法返回字符的編碼值。

let s1 = String::from("hboot");// 遍歷獲取字符for c in s1.chars() {    println!("{c}");}// 遍歷獲取字節(jié)for c in s1.bytes() {    println!("{c}");}

對(duì)于字節(jié),有的語(yǔ)言編碼后可能不止一個(gè)字節(jié)組成,這個(gè)需要注意。

HashMap存儲(chǔ)鍵值對(duì)

創(chuàng)建HashMap實(shí)例,因?yàn)?HashMap 沒(méi)有被 prelude。所以需要手動(dòng)引入。

use std::collections::HashMap;fn main(){    let mut map = HashMap::new();}

當(dāng)未被使用時(shí),鍵值對(duì)的數(shù)據(jù)類型是unknown。在第一次插入數(shù)據(jù)后,則決定了后面的數(shù)據(jù)類型

let mut map = HashMap::new();map.insert(1, 10);map.insert(2, 30);

此時(shí)默認(rèn)類型為HashMap。當(dāng)時(shí)用 String 作為鍵值是,變量的所有權(quán)將被轉(zhuǎn)移給 map。字符串變量不可用

let mut map = HashMap::new();let s = String::from("red");map.insert(s, "red");

通過(guò)map.get()獲取 HashMap 中的值,返回Option<&V>,如果沒(méi)有鍵時(shí),則返回None.

可以通過(guò)copied()方法來(lái)獲取Option;如果沒(méi)有鍵時(shí),可以通過(guò)uwrap_or()在沒(méi)有鍵值時(shí),設(shè)置一個(gè)替代值。

map.get(&String::from("yellow")).copied().unwrap_or("yellow");

注意get方法接受是一個(gè)&str類型。

當(dāng)我們重復(fù)對(duì)同一個(gè)鍵賦值時(shí),后面的會(huì)覆蓋之前的。如果需要判斷是否存在鍵,不存在插入數(shù)據(jù);存在則不做任何操作

map.entry(String::from("green")).or_insert("green");

entryor_insert()方法在鍵存在時(shí)會(huì)返回這個(gè)值的可變引用。不存在則將參數(shù)作為新值插入并返回值的可變引用。

一個(gè)示例,通過(guò) HashMap統(tǒng)計(jì)字符串中出現(xiàn)的字符數(shù)。

let s1 = String::from("hboot");let mut map = HashMap::new();for c in s1.chars() {    let num = map.entry(c).or_insert(0);    *num += 1;}dbg!("{:?}", map);

HashMap默認(rèn)使用了叫做 SipHash 的哈希函數(shù),可以抵御哈希表的拒絕服務(wù)攻擊。

泛型、trait 和生命周期

泛型是具體類型和其他屬性的抽象替代。定義時(shí)不必知道這里實(shí)際代表什么,比如之前的實(shí)例中的Option / Vec都已經(jīng)接觸了。

泛型

通過(guò)定義泛型,可以抽離一些重復(fù)的代碼邏輯。使得我們的代碼更具維護(hù)性、適應(yīng)性更強(qiáng)。

創(chuàng)建一個(gè)泛型函數(shù)。類型參數(shù)聲明必須在函數(shù)名稱和參數(shù)列表中間尖括號(hào)<>里面。

fn largest(list: &[T]) -> &T {    let mut large = &list[0];    for val in list {        if val > large {            large = val;        }    }    large}fn main(){    let v1 = vec![12, 34, 5, 56, 7];    let v2 = vec![34.23, 12.12, 56.1223, 23.12];    dbg!(largest(&v1));    dbg!(largest(&v2));}

實(shí)例中為了找出給定 vector 結(jié)構(gòu)數(shù)據(jù)中的最大值。但是調(diào)用的兩次結(jié)構(gòu)實(shí)例是不同的數(shù)據(jù)類型i32、f64,使用泛型則可以只寫(xiě)一個(gè)公用的函數(shù)。

泛型函數(shù)中通過(guò)遍歷結(jié)構(gòu)中的數(shù)據(jù)進(jìn)行對(duì)比排序。但是泛型是任何類型,存在有的數(shù)據(jù)類型不能進(jìn)行排序,rust 在編譯階段會(huì)報(bào)錯(cuò)。所以增加了泛型限制,std::cmp::PartialOrd標(biāo)識(shí)傳入的類型都可以進(jìn)行排序。

在結(jié)構(gòu)體使用泛型,作為數(shù)據(jù)類型。

struct Size{    width:T,    height:T}

也可以傳入多個(gè)泛型,對(duì)應(yīng)不同的字段數(shù)據(jù)類型Size

在枚舉中使用泛型。之前已經(jīng)使用的枚舉Option

enum Status {    YES(T),    NO(U),}

也可以在結(jié)構(gòu)體、枚舉的方法定義中使用泛型。此時(shí)需要在impl后聲明泛型T

impl Size {    fn width(&self) -> &T {        &self.width    }}

如果在方法中,指定了具體的數(shù)據(jù)類型,那么創(chuàng)建的實(shí)例,不是該數(shù)據(jù)類型時(shí),則不能調(diào)用該方法。

impl Size {    fn height(&self) -> &u8 {        &self.height    }}fn main(){    let size1: Size = Size {        width: 34,        height: 45,    };    let size2: Size = Size {        width: 34.12,        height: 45.34,    };    size1.height(); // size1 實(shí)例上有height方法。size2則沒(méi)有}

泛型不會(huì)使程序比具體類型運(yùn)行的慢。rust 通過(guò)在編譯時(shí)進(jìn)行泛型代碼的單態(tài)化,也就是重復(fù)將泛型聲明為具體的定義。

trait定義共同行為

什么是 trait,在之前的描述已多次出現(xiàn)。它定義了某個(gè)特定類型擁有可能與其他類型相同的功能。

  • 可以通過(guò)trait以一種抽象的方式定義共享的行為。
  • 可以使用trait bounds指定泛型是任何擁有特定行為的類型。

類比接口行為。抽象定義屬性、方法,然后其他的實(shí)例創(chuàng)建實(shí)現(xiàn)接口中的方法。

通過(guò)trait定義一個(gè)抽象方法。

trait Log {    fn log(&self)->String;}

聲明一個(gè)Logtait,包含了一個(gè)方法 log。它用來(lái)記錄實(shí)例創(chuàng)建產(chǎn)生行為后日志記錄。

每個(gè)聲明的集合數(shù)據(jù)都必須實(shí)現(xiàn)這個(gè)方法。

struct Size {    width: T,    height: T,}// std::fmt::Debug 是為了打印輸出impl Log for Size {    fn log(&self) -> String {        let str = format!("{:?}-{:?}", &self.width, &self.height);        println!("變更值:{str}");        str    }}fn main(){    let mut size2: Size = Size {        width: 34.12,        height: 45.34,    };    size2.width = 45.111;    size2.log();}

也可以提供一個(gè)默認(rèn)實(shí)現(xiàn),這樣可以選擇重載這個(gè)方法或者保留默認(rèn)實(shí)現(xiàn)。

trait Log {    fn entry_log(&self) -> String {        String::from("entry log...")    }}

然后在其他類型實(shí)現(xiàn) trait 時(shí),可以保留默認(rèn)的行為。

// 在上方實(shí)現(xiàn)的結(jié)構(gòu)體size2,可以直接調(diào)用println!("{}", size2.entry_log());

也可以在默認(rèn)實(shí)現(xiàn)中,調(diào)用其他方法。

trait Log {    fn log(&self) -> String;    fn entry_log(&self) -> String {        let entry = String::from("entry log...");        println!("{}", entry);        // 調(diào)用log方法        let content = self.log();        format!("{}", content)    }}fn main(){    // size2 實(shí)現(xiàn)不變,僅需要調(diào)用entry_log方法即可    // entry.log();    size2.entry_log();}

實(shí)現(xiàn)了trait這些定義后,如何將其作為參數(shù)傳遞呢。使用impl trait語(yǔ)法

fn notify(item: &impl Log) {    println!("Log! {}", item.entry_log());}fn main(){    // 通過(guò)傳遞實(shí)例 size2直接調(diào)用該方法    notify(&size2);}

也可以通過(guò)泛型來(lái)定義參數(shù),專業(yè)術(shù)語(yǔ)稱為trait bound

fn notify(item: &T) {    println!("Log! {}", item.entry_log());}

這種方式在對(duì)于多個(gè)參數(shù)的書(shū)寫(xiě)友好??梢酝ㄟ^(guò)泛型限制參數(shù)的類型。

fn notify(item: &T,item1:&T) {    println!("Log! {}", item.entry_log());}

也可以通過(guò)+指定多個(gè) trait。

fn notify(item: &(impl Log + Display)) {}// 或者使用泛型fn notify(item: &T) {}

調(diào)用傳參時(shí)的實(shí)例則必須實(shí)現(xiàn)Log和Display,但是當(dāng)有很多個(gè) trait 時(shí),書(shū)寫(xiě)起來(lái)就會(huì)很多。

可以通過(guò)where關(guān)鍵字簡(jiǎn)化書(shū)寫(xiě),看起來(lái)更加的清晰。

fn notify(item: &T, item2: &U)where    T: Log + Display,    U: Clone + Display,{}

也可以通過(guò)函數(shù)返回某個(gè)實(shí)現(xiàn)了trait的類型實(shí)例

fn return_log() -> impl Log {    Size {        width: 23,        height: 45,    }}fn main(){    let size3 = return_log();    size3.entry_log();}

在閉包和迭代器場(chǎng)景中十分有用。但是這種適用于返回單一類型的情況。

通過(guò)trait bound可以有條件的控制實(shí)例可調(diào)用的類型方法。只有類型實(shí)現(xiàn)了某些方法,實(shí)例才會(huì)有指定的方法。

生命周期

也就是對(duì)于引用、借用的有效作用域的限制。在引用或借用之前,保證被引用或借用的變量在當(dāng)前作用域一直有效。

這個(gè)特性避免了懸垂引用,防止了程序引用未定義數(shù)據(jù)的問(wèn)題;如下例子:

fn main(){    let a;    {        let b = "admin";        a = &b;    }    println!("{}",a)}

運(yùn)行cargo run這段代碼,將會(huì)報(bào)錯(cuò),變量a得到了局部作用域變量b的引用,在最后的作用域中使用了a。但是變量b在局部作用域結(jié)束時(shí)就已經(jīng)釋放了,導(dǎo)致引用它的a在使用時(shí)就會(huì)報(bào)錯(cuò)。

在 rust 中,通過(guò)借用檢查器來(lái)檢測(cè)作用域之間的借用是否都是有效的。并在編譯階段給出錯(cuò)誤提示,上面的代碼不需要運(yùn)行,也可以看到編譯器給出的錯(cuò)誤提示。

為了解決上面這問(wèn)題,我們可以將 b的所有權(quán)交出去,因?yàn)?code>b作用域結(jié)束,并沒(méi)有什么作用了。

{    let b = "admin";    a = b;}

還有在第一篇文章所有權(quán)的問(wèn)題

fn print_info() -> String {    let str = String::from("hboot");    // 這是錯(cuò)誤的,函數(shù)執(zhí)行完畢,必須交出所有權(quán)    // &str    // 直接返回創(chuàng)建的字符串    str}

生命周期注解

還有一些問(wèn)題,在函數(shù)調(diào)用的時(shí)候,需要傳參處理完后返回某個(gè)參數(shù)的值。如下示例:

fn main(){    let a = String::from("abcd");    let b = String::from("efg");    println!("{}", longest(&a, &b));}fn longest(a: &str, b: &str) -> &str {    if a.len() > b.len() {        a    } else {        b    }}

編譯器直接就會(huì)提示錯(cuò)誤信息,我們執(zhí)行cargo run看詳細(xì)的錯(cuò)誤信息。錯(cuò)誤也很明確expected named lifetime parameter,并且給出了解決示例。

fn longest<"a>(a: &"a str, b: &"a str) -> &"a str {    if a.len() > b.len() {        a    } else {        b    }}

"a就是生命周期的注解語(yǔ)法。

  • 它可以給描述多個(gè)引用生命周期的關(guān)系,而不影響參數(shù)a、b的生命周期。
  • 在函數(shù)指定了泛型生命周期,函數(shù)就可以接受任何生命周期的引用。
  • 生命周期參數(shù)以"撇號(hào)開(kāi)頭,小寫(xiě)的名稱。
  • 它位于引用&之后,它表明了被借用的變量存在的時(shí)間和借用變量的生命周期存在的一樣久
  • 它保證了在引用值a、b中作用域最短的那個(gè)生命周期結(jié)束之前有效

需要注意的的就是最后一個(gè),它的存在時(shí)間長(zhǎng)久在于作用域最短的那一個(gè)

fn main(){    let a = String::from("abcd");    let result;    {        let b = String::from("efg");        result = longest(&a, &b);    }    println!("{}", result);}

函數(shù)的調(diào)用在b的局部作用域中,調(diào)用結(jié)束后的結(jié)果值result使用超出了b的作用域,編譯器報(bào)錯(cuò)。

可以把result的使用范圍局限在b的作用域內(nèi)。

{    let b = String::from("efg");    result = longest(&a, &b);    println!("{}", result);}

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

同設(shè)置泛型一樣,在結(jié)構(gòu)體名稱后面使用簡(jiǎn)括號(hào)<>聲明泛型生命周期。

struct User<"b> {    name: &"b str,}fn main(){    let name = String::from("hboot");    let user = User { name: &name };}

結(jié)構(gòu)體的實(shí)例user的生命周期不能比字段name的引用存在的更久。

生命周期注解是為了 rust 檢查器推斷出引用的生命周期。有時(shí)候就會(huì)書(shū)寫(xiě)大量的這種模板式的注解,這種場(chǎng)景有時(shí)候 rust 會(huì)納入到編譯器中,這樣就不在顯示聲明,而這些模式統(tǒng)稱為生命周期省略規(guī)則。我們?cè)跁?shū)寫(xiě)時(shí),只要總訓(xùn)這些規(guī)則就可以不用聲明式書(shū)寫(xiě)生命周期了。

編譯器推斷生命周期的規(guī)則:

  1. 編譯器為每一個(gè)輸入的參數(shù)都分配一個(gè)生命周期參數(shù)
  2. 如果只有一個(gè)輸入?yún)?shù),那么它的生命周期參數(shù)賦予給所有的輸出生命周期
  3. 如果有多個(gè)輸入?yún)?shù),其中之一個(gè)參數(shù)是&self &mut self,所有輸出生命周期被賦予 self 的生命周期。

static靜態(tài)生命周期

通過(guò)static聲明一個(gè)靜態(tài)生命周期,它存活于整個(gè)程序運(yùn)行期間。

let str:&"static str = "hello rust";

str文本直接存儲(chǔ)在程序的二進(jìn)制文件中。在使用時(shí)考慮是否真的需要。

包、create

通過(guò)拆解模塊來(lái)創(chuàng)建多個(gè)文件組織代碼。更好的重用代碼,定義哪些內(nèi)容可以公開(kāi),哪些是私有的。

這里有一些概念:- Cargo 的一個(gè)功能,允許構(gòu)建、測(cè)試和分享 crate。crates- 一個(gè)模塊的樹(shù)形結(jié)構(gòu),它形成了庫(kù)或者二進(jìn)制項(xiàng)目。模塊/use- 允許控制作用域和路徑的私有性。路徑- 命名例如結(jié)構(gòu)體、函數(shù)或模塊等方式

包、crate

crate分為庫(kù)和二進(jìn)制。二進(jìn)制可以被編譯為可執(zhí)行文件,有一個(gè)main函數(shù)來(lái)執(zhí)行程序需要做的事情;庫(kù)用來(lái)作為工具,提供諸如函數(shù)的功能。

包是一些列功能的一個(gè)或多個(gè)crate。包含Cargo.toml文件,闡述如何去構(gòu)建這些 crate。

往往src/lib.rs就表示這是一個(gè)庫(kù);而src/main.rs表示這是一個(gè)包。這也是編譯時(shí)的入口點(diǎn)。

通過(guò)mod聲明一個(gè)模塊,通過(guò)內(nèi)聯(lián)方式聲明mod user{};或者創(chuàng)建文件src/user.rs或者src/user/mod.rs

// 內(nèi)聯(lián)聲明mod user {}

聲明好模塊后,要想在其他地方使用該模塊,則需要加pub修飾,標(biāo)識(shí)這是一個(gè)公用模塊。

假設(shè)現(xiàn)在我們有以文件創(chuàng)建的模塊src/user.rs,其中有兩個(gè)聲明的公用結(jié)構(gòu)和枚舉類。

pub struct model {    name: String,    age: i32,}pub enum status {    online,    offline,}

通過(guò)mod關(guān)鍵字定義,來(lái)說(shuō)明編譯器在src/user.rs查找代碼。

// 在main.rs中mod user;fn main(){    // 可以直接通過(guò)模塊名稱來(lái)使用定義在模塊中的類型    let status = user::status::offline;}

也可以通過(guò)use關(guān)鍵字來(lái)導(dǎo)入需要使用的公用類型。

// 在main.rs中use crate::user::status;mod user;fn main(){    let status = status::offline;}

在一個(gè)模塊中,也可以繼續(xù)聲明子模塊。聲明的方式同上

引用模塊路徑

剛才使用模塊引入的方式crate::user是以 crate 跟開(kāi)頭的全路徑。也可以相對(duì)于當(dāng)前模塊開(kāi)始,以self或者super

// 在main.rs中use user::status;mod user;

由于模塊 user 和main.rs是在同一路徑下,所以可以通過(guò)相對(duì)路徑引入。

如果模塊層級(jí)嵌套,不在同一路徑下,要想使用相對(duì)路徑,可通過(guò)super相當(dāng)于..,從父模塊的路徑引入。

// 在模塊user中定義子模塊mod work {    use super::model;    use super::status;    fn is_working(user: model) -> String {        match user.status {            status::online => String::from("在線"),            status::offline => String::from("離線"),        }    }}

雖然它們?cè)谕晃募?,但?code>work定義為子模塊,有自己的作用域。所以不能直接訪問(wèn)父模塊中定義的類型??赏ㄟ^(guò)super引用。

self則表示自己,調(diào)用自己模塊中的定義。

mod work {    use super::model;    use super::status;    fn is_working(user: model) {        // 直接調(diào)用        init_user();        // 通過(guò)self調(diào)用        self::init_user();    }    fn init_user() {}}

pub聲明的公用方法、類型,對(duì)于結(jié)構(gòu)體,它的字段卻是私有的。如果想要?jiǎng)?chuàng)建實(shí)例,則必須聲明字段為公用。

// 在main.rs中use user::{model, status};mod user;fn main(){    // 下面這個(gè)創(chuàng)建時(shí)編譯不過(guò)的,錯(cuò)誤提示字段私有。    let u = model {        name: String::from("admin"),        age: 35,        status: status::offline,    };}

但是對(duì)于子模塊引入使用時(shí),這些字段默認(rèn)都是有效可用的。

mod work {    use super::model;    use super::status;    fn init_user() {        let user = model {            name: String::from("hboot"),            age: 34,            status: status::online,        };    }}

對(duì)于外部引入模塊的結(jié)構(gòu)體時(shí),如果有私有屬性,則需要提供實(shí)例化方法。

// 調(diào)整src/user.rs ,提供實(shí)例化方法pub struct model {    pub name: String,    age: i32,    status: status,}impl model {    pub fn new(name: String) -> Self {        Self {            name,            age: 35,            status: status::online,        }    }}// 在src/main.rsfn main(){    let u = model::new(String::from("hboot"));}

通過(guò)use引入,如果遇到同名類型時(shí),引入路徑可以只寫(xiě)到模塊名稱,然后通過(guò)模塊名稱調(diào)用方法、類型。

// 在main.rs中use user::work;mod user;fn main(){    let u2 = work::init_user();    print!("{:?}", u2)}

注意上面實(shí)例可被打印,修改模塊 user 定義的結(jié)構(gòu)體、枚舉#[derive(Debug)]

也可通過(guò) as關(guān)鍵字提供一個(gè)別名。

// 在main.rs中use user::work::init_user as initUser;mod user;fn main(){    let u2 = initUser();}

可通過(guò)pub use繼續(xù)導(dǎo)出到外部作用域使用。這可以避免路徑過(guò)長(zhǎng)引入,可以將子模塊的定義導(dǎo)入到父級(jí)模塊中。再重導(dǎo)出。

pub use user::work;

這樣對(duì)于當(dāng)前作用域路徑的上級(jí),可以繼續(xù)導(dǎo)入 work 模塊使用。

當(dāng)一個(gè)功能模塊子模塊很多時(shí),就需要從一個(gè)模塊中導(dǎo)出很多的類型、結(jié)構(gòu)體、方法等。就會(huì)出現(xiàn)很多use行,使用嵌套路徑路徑消除這種引入。

上面的示例已經(jīng)展示了如何引入多個(gè)定義類型。

use user::model;use user::status;// 嵌套一行搞定use user::{model, status};

也可以通過(guò) glob 運(yùn)算符*導(dǎo)入所有的公用項(xiàng)。

use crate::user::*

錯(cuò)誤處理

在程序遇到錯(cuò)誤時(shí),分為可恢復(fù)和不可恢復(fù)??苫謴?fù)問(wèn)題比如訪問(wèn)數(shù)據(jù)、文件未訪問(wèn)到,可通過(guò)日志方式告知用戶;不可恢復(fù)問(wèn)題比如越界,需要中止執(zhí)行。

通過(guò)Result處理可恢復(fù)的錯(cuò)誤;panic!宏處理不可恢復(fù)的錯(cuò)誤。終止程序執(zhí)行。

通過(guò)panic!可以直接拋出一個(gè)錯(cuò)誤。

fn main() {    panic!("hello world");}

程序執(zhí)行到此處會(huì)終止執(zhí)行,并報(bào)出錯(cuò)誤打印從出 hello world。可以看到錯(cuò)誤出現(xiàn)的代碼位置信息

通過(guò)錯(cuò)誤提示,可以設(shè)置環(huán)境變量RUST_BACKTRACE=1,查看調(diào)用棧信息

$> RUST_BACKTRACE=1 cargo run

發(fā)布生產(chǎn)環(huán)境包時(shí),可以將panic禁止掉,從而得到更小的二進(jìn)制文件。

# Cargo.toml[profile.release]panic="abort"

處理可恢復(fù)的錯(cuò)誤

有一些錯(cuò)誤不影響程序允許的情況,我們需要給出錯(cuò)誤時(shí)得處理方案。

Result枚舉類,標(biāo)識(shí)程序方案按預(yù)期或者錯(cuò)誤。

enum Result {    Ok(T),    Err(E),}

Result及其成員被提前導(dǎo)入。

比如讀取文件時(shí),如果文件存在,則讀取成功,狀態(tài)為Ok,類型 T 則為std::fs::File文件

use std::fs::File;fn main(){    let read_fs = File::open("hello.txt");    // 通過(guò)match    let read_file = match read_fs {        Ok(file) => file,        Err(error) => panic!("error info:{:#?}", error),    };}

因?yàn)槲覀兾募夸浵聸](méi)有hello.txt文件,就會(huì)執(zhí)行中斷,報(bào)錯(cuò)。我們處理錯(cuò)誤時(shí),如果是文件未找到,則直接從創(chuàng)建一個(gè)。

use std::fs::File;use std::io::ErrorKind;fn main(){    let read_fs = File::open("hello.txt");    let read_file = match read_fs {        Ok(file) => file,        Err(error) => match error.kind() {            ErrorKind::NotFound => match File::create("hello.txt") {                Ok(f) => f,                Err(err) => panic!("error in create file:${:?}", err),            },            other_error => {                panic!("error info:{:#?}", error)            }        },    };}

執(zhí)行后,在項(xiàng)目根目錄下會(huì)生成hello.txt文件。如果是沒(méi)有權(quán)限訪問(wèn)時(shí),則還是打印輸出錯(cuò)誤。

我們寫(xiě)了很多的match來(lái)處理不同的情況,這看起來(lái)很讓人難以理解。通過(guò)unwrapexpect簡(jiǎn)寫(xiě)處理

unwrap()方法調(diào)用,如果文件訪問(wèn)到,則返回Ok;讀取不到則返回Err.

let read_file = File::open("hello.txt").unwrap();

通過(guò)expect()方法調(diào)用可以達(dá)到同樣的功能。但是它允許我們自定義錯(cuò)誤信息。

let read_file = File::open("hello.txt").expect("無(wú)法讀取 hello.txt!!!");

有一些錯(cuò)誤在每個(gè)方法中處理重復(fù)、麻煩??梢詫㈠e(cuò)誤信息傳遞到調(diào)用方,然后統(tǒng)一處理。

use std::fs::File;use std::io::{self, ErrorKind, Read};fn read_file() -> Result {    let file_result = File::open("hello.txt");    let mut file_name = match file_result {        Ok(file) => file,        Err(e) => return Err(e),    };    let mut name = String::new();    match file_name.read_to_string(&mut name) {        Ok(_) => Ok(name),        Err(e) => Err(e),    }}fn main(){    let read = read_file();    println!("{:?}", read)}

hello.txt中寫(xiě)一段話,則會(huì)被打印出來(lái)。刪除hello.txt文件,則打印的是錯(cuò)誤信息。

通過(guò)運(yùn)算符?簡(jiǎn)寫(xiě),處理錯(cuò)誤信息,替代match的返回錯(cuò)誤信息。

fn read_file() -> Result {    let mut file_name = File::open("hello.txt")?;    let mut name = String::new();    file_name.read_to_string(&mut name)?;    Ok(name)}

使用?簡(jiǎn)化了很多代碼,還可以通過(guò)鏈?zhǔn)秸{(diào)用操作,使代碼更為簡(jiǎn)短。

fn read_file() -> Result {    let mut name = String::new();    File::open("hello.txt")?.read_to_string(&mut name)?;    Ok(name)}

rust 還提供了更為方便的寫(xiě)法,fs::read_to_string,它做了這些事情:打開(kāi)文件、新建一個(gè) String、讀取文件的內(nèi)容。將內(nèi)容放入 String,并返回它。

fn read_file() -> Result {    fs::read_to_string("hello.txt")}

?運(yùn)算符只能用在返回值為Result類型的方法中。

什么情況下使用panic!

在更多的情況,我們都希望程序不要中斷執(zhí)行,所以處理結(jié)果返回Result類型是最好的選擇。

還有一些,希望不執(zhí)行的情況。

  • 示例代碼、測(cè)試運(yùn)行出錯(cuò)時(shí),可以中斷執(zhí)行,執(zhí)行panic!
  • 當(dāng)我們很明確某種情況下程序是不能繼續(xù)運(yùn)行的。并給出錯(cuò)誤信息和其他有用的信息,
  • 創(chuàng)建自定義類型進(jìn)行有效性驗(yàn)證

panic!代表了無(wú)法處理的錯(cuò)誤。停止執(zhí)行以防止代碼繼續(xù)執(zhí)行出現(xiàn)的不可預(yù)估的錯(cuò)誤。

關(guān)鍵詞:

Copyright   2015-2022 歐洲城建網(wǎng) 版權(quán)所有  備案號(hào):滬ICP備2022005074號(hào)-23   聯(lián)系郵箱: 58 55 97 3@qq.com

亚洲乱码一区二区三区在线观看| 无码少妇精品一区二区免费动态| 97人人爽人人爽乱码av国产 | 欧美性猛交xxxx| 国产精品久久久久久久9999| 国产精品欧美成人片| 亚洲午夜福利在线观看| 一区二区三区内射美女毛片 | 亲嘴脱内衣内裤摸屁股| 亚洲码欧美码一区二区三区| 熟女视频一区二区在线观看| 国产综合精品| 男男肠道灌水失禁play| 激情五月综合色婷婷一区二区| 久久久久久人妻无码| 亚洲欧美精品午睡沙发| 精品人人妻人人澡人人爽牛牛| 国产成人无码aa精品一区| 办公室双腿打开揉弄高潮淑芬| 亚洲欧美日韩综合久久久| 久久久欧美国产精品人妻噜噜 | 亚洲av人无码激艳猛片服务器| 欧美狂野另类xxxxoooo| 国产精品国产三级国产a| 亚洲日韩一区二区三区四区高清 | 亚洲乱码日产精品BD在线观看| 污污污www精品国产网站| 国产特级毛片aaaaaa| 久久久国产精品人人片| 和尚吮她的花蒂和奶水视频| 精品熟女60老妇av免| 国产麻传媒精品国产av| 无码国产精品一区二区免费虚拟vr | 天天干天天射天天操| 国产乱妇无码大片在线观看| 亚洲av中文无码乱人伦下载 | 久久青青草原亚洲av无码麻豆| 成熟丰满熟妇高潮xxxxx视频| 性欧美丰满熟妇xxxx性久久久| 高中女学生破苞视频免费 | 97人伦影院a级毛片|