日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

rust为什么显示不了国服_Rust编程语言初探

發(fā)布時間:2023/12/1 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 rust为什么显示不了国服_Rust编程语言初探 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

靜態(tài)、強類型而又不帶垃圾收集的編程語言領(lǐng)域內(nèi),很久沒有新加入者參與競爭了,大概大部分開發(fā)者認為傳統(tǒng)的C/C++的思路已經(jīng)不太適合新時代的編程需求,即便有Ken Tompson這樣的大神參與設(shè)計的golang也采用了GC的思路來設(shè)計其新一代的語言;一方面垃圾收集技術(shù)和即使編譯技術(shù)一直在發(fā)展和完善,另一方面是大量的未經(jīng)過嚴格計算機科學基礎(chǔ)訓(xùn)練的開發(fā)人員進入市場,似乎讓開發(fā)者永遠停留在邏輯層面而不是去直接操縱內(nèi)存是個更為現(xiàn)代的選擇,Mozilla卻仍然堅信一門靜態(tài)而又高效低利用系統(tǒng)資源的“偏底層”的語言也依然會有巨大的生命力;于是站在現(xiàn)代成熟的軟件工程實踐上的Rustlang(以下簡稱Rust)為創(chuàng)造出來,其新版本的發(fā)布不時引起HackNews等極客圈的關(guān)注。

本文試圖通過其官方文檔對該語言(以及其相關(guān)的生態(tài)系統(tǒng))做簡單的研習。

核心語言特性設(shè)計目標

按照其官方描述,Rust需要滿足其以下幾個核心目標

  • 適用于系統(tǒng)編程場景 - 這意味著能夠有直接訪問操作系統(tǒng)基礎(chǔ)設(shè)施和硬件的能力。
  • 開源參與和協(xié)作 - 畢竟其背后的推動者是創(chuàng)造了第一代瀏覽器的Mozilla,比任何商業(yè)公司更懂得依靠開源社區(qū)的力量
  • 安全而又高效 - 在現(xiàn)代的軟件工程環(huán)境中,安全是不可或缺的,而面向系統(tǒng)編程場景的語言必然少不了對性能的極致要求
  • 充分利用現(xiàn)代多核和并發(fā)處理技術(shù)的能力 - 這也是傳統(tǒng)的C/C++語言的軟肋所在;同時也是Google的Golang的設(shè)計目標之一
  • 容易學習的語法 - 減少類似于段錯誤或隱式的多線程編程等相對底層的細節(jié)應(yīng)該盡量被隱藏
  • 根據(jù)以上目標可以相對容易的理解一些核心的語言設(shè)計策略背后的決策依據(jù)。

    基本語法特性

    作為一門面向系統(tǒng)編程的偏底層的程序語言,其基本語法和傳統(tǒng)的C/C++/Java系列語言共享了很多共同之處,這里僅需要看看其不同之處。

    類型系統(tǒng)

    靜態(tài)語言的基本元素之一是變量和類型;不同的語言會選擇不同的類型定義和內(nèi)置的開箱可用的基本類型;這些類型及內(nèi)置類的設(shè)計往往反映了編程語言設(shè)計者的決策策略和權(quán)衡要素。

    類型聲明和自動推斷

    畢竟要面對的是偏嚴肅的系統(tǒng)編程領(lǐng)域,選擇靜態(tài)類型可以在編譯階段盡可能早地發(fā)現(xiàn)更多的程序錯誤是題中之義;同時作為一門比較現(xiàn)代的編程語言,每次讓程序員自己輸入每個變量的類型這類臃腫的做法也被廢棄,自動類型推斷必不可少 - 當編譯器可以”聰明地”推導(dǎo)出合適的類型的時候,變量類型指定可以忽略。

    譬如需要聲明一個某種類型的變量,Rust用let x: someType = 來表示;當然對于編譯器可以推導(dǎo)出來類型的情況下,類型是可以省略的這樣可以少寫一些啰嗦的代碼,let x = 2就會定義一個整數(shù)類型的變量x;比較新的編程語言基本都是這么做的,沒有什么新意。

    作為一門強類型的語言,任何變量或者表達式必須有唯一的類型,否則編譯的時候就會報錯。當然Rust支持一種特殊的變量隱藏機制(Shadow),即同一個名字的變量可以重新使用,并設(shè)置為一個完全不同的類型;這個時候原來的變量就不能被訪問了。如

    let var = "something"; //string literallet var = 1; //changed to int

    這種機制從某種程度上來說,反而會使代碼變得不太容易理解,如果程序員習慣了C/C++的編程方式的話;同時也會給IDE等工具的解析帶來一些挑戰(zhàn);當然這個是仁者見仁智者見智的事情。

    類型可變性約束

    Rust要求所有定義的變量必須指定是否是可變的;并且作為變量的基本特征強制程序員做合理的選擇。可變的變量用類似let mut varName:Type = value的語法來定義,顧名思義可以在聲明之后被重新賦值修改;而不可變的變量少了一個mut關(guān)鍵字,其定義的變量在初始化一次后續(xù)就不能再修改了。

    Rust里邊同時支持常量類型,用const來聲明,像是從C++里借鑒來的。它和可變類型mutable有一些細微的不同: 對于常量類型我們必須使用類型注解,不能聲明可變的常量類型(不允許混合const和mut),而且常量類型只能被賦值為一個常量表達式, 不能用函數(shù)調(diào)用的結(jié)果或者是其他一些運行時計算出來的值來初始化。綜合來看,Rust的常量類型和C++11中新引入的constexpr行為比較接近。

    內(nèi)置類型

    內(nèi)置類型一般用于提供大部分程序員都要用到的基本數(shù)據(jù)結(jié)構(gòu)。除了一些其他語言都常見的基本類型(Rust稱之為標量類型),Rust也提供了一些相對比較復(fù)雜的類型。

    基本標量類型包含以下這些基本的類型

    • 整型類型,包括定長的8/16/32/64位的有符號和無符號類型(如u16是無符號16位整型,i32是有符號32位類型), 還支持平臺相關(guān)的有符號/無符號類型,分別用isize和usize表示
    • 浮點類型,支持單精度(f32)和雙精度(f64)類型,這些在數(shù)值計算的時候比較關(guān)鍵
    • 布爾類型,和C++中的比較類似,有true和false兩種可能的取值,當然沒有C/C++中的那些隱式轉(zhuǎn)換的麻煩
    • 字符類型,支持Unicode

    復(fù)合類型`

    比較新一點的語言都支持一些復(fù)雜一點的基本組合類型。

    tuple和其它語言的比較類似,用括號語法來聲明,基本用法可以看下邊這個簡單的例子

    let tup = (1, 2.2, "something")let (a, b, c) = tuplet secondElem = tup.1

    第一行代碼聲明一個含有三個不同類型的元素的元組;第二行代碼則將元組中的元素逐一取出,和Python的用法比較類似。除了這種提取方式,元組元素也可以用點語法來訪問元素,如上邊的第三行代碼則用tup.1則取出第二個元素;比C++11的模板元語法簡單多了。

    數(shù)組則用于表示具有相同類型的元素的集合,如let arr = [1, 2, 3, 4, 5],如果類型不一致則會有編譯錯誤報出。和C/C++這中的類似,數(shù)組元素一般是分配在棧上的,其大小在編譯器應(yīng)該是預(yù)先確定的;如果需要可變長的容器,則需要Vector類型。數(shù)組越界的檢查默認也包含在語言中了,如果訪問越界的下標,默認程序就會崩潰;當然Rust的錯誤處理機制也有些特殊,容后探討。

    容器類型

    Rust支持以下基本的容器類型

    • Vector 該類型用于存儲邏輯上的列表類型,其正式名字是Vec,用Vec::new()創(chuàng)建空的向量,因為其是用泛型實現(xiàn)的,我們必須指定類型注解;即使用 let v: Vec = Vec::new() 來生成一個新的向量v
    • String 是作為一個庫提供的而不是基本的語言機制;其實現(xiàn)和C++的比較類似,內(nèi)部也使用一個Vec來存儲數(shù)據(jù)的,因此考慮到國際化的原因,其操作可能比其它語言中的要復(fù)雜一些;幸運的是,這些細節(jié)以及被標準庫所封裝。
    • Hashmap 用于表述邏輯上的哈希關(guān)聯(lián)容器;其提供的API和C++/Java的比較類似,功能上比C++的復(fù)雜一些但比Java的更精簡一點

    函數(shù)

    作為基本編程要素的函數(shù)在Rust中的定義沒有什么特別特殊的地方,除了其類型聲明是后置風格之外,其返回類型(如果不能被自動推斷)用->來聲明,比如

    //一個返回int類型的函數(shù)fn thisIsAFunction(parA: i32, parB: string) -> int { //some implementation}

    函數(shù)的實現(xiàn)體本質(zhì)上是一個block,由一系列的表達式組成(當然表達式也是用分號分隔的),同時它還支持Ruby風格的自動返回最后一個表達式的寫法, 僅僅需要最后一個表達式省略分號即可;比如這個簡單的函數(shù)

    fn five() -> i32 { 5}

    懶惰是偉大程序員的優(yōu)良品質(zhì)嘛。由于我們有內(nèi)置的tuple類型,因此Rust是可以允許有多個返回值的;比較典型的一個場景是用戶錯誤處理的情況,可以返回一個Result,同時攜帶錯誤碼和可能的原因;稍后會仔細看一下異常處理的部分。

    函數(shù)和宏

    Rust本身支持語法層面的宏,并且其標準庫提供了很多各種各樣的宏,譬如最常用的打印函數(shù)其實就是一個宏;所有的宏使用!后綴來區(qū)分。 println!("The value of x is {}, y is {}", x, y)用于打印出x和y的值;其語法形式非常像一些常見的Java庫所支持的格式,可以用大括號來打印對象。

    宏是在編譯的早期階段被展開的,和C中的宏原理類似,雖然Rust的語法看起來更簡潔一些;但是依然有很多新的語法構(gòu)造,簡單來說可以認為Rust的宏是用macro_rules和模式匹配來實現(xiàn)的。

    從可維護的角度來說,應(yīng)該做好這種因為宏代碼往往意味著更難理解和調(diào)試。很多時候,需要將宏作為最后一種不得已而為之的措施。比C中的宏好一點的是,Rust提供了對宏進行調(diào)試的方式,可以在其編譯器的命令行中加入--pretty expand選項來查看展開的代碼。

    錯誤檢查機制

    現(xiàn)實生活中的軟件總是有各種各樣的錯誤需要被正確處理但沒有被及早處理就泄漏到了客戶現(xiàn)場。Rust采用的設(shè)計思路是,盡早強迫程序員去顯示處理并以編譯器錯誤的方式提示程序員。

    和Java的關(guān)于錯誤分類的思路類似,Rust也區(qū)分可恢復(fù)的錯誤和不可恢復(fù)的錯誤,并提供了相應(yīng)的語言機制上的支持。可恢復(fù)的錯誤一般是一些環(huán)境的錯誤,譬如文件找不到或者網(wǎng)絡(luò)連接失敗等情況,實現(xiàn)上可以用重試等策略來嘗試自動恢復(fù)。不可恢復(fù)的錯誤往往意味著編程錯誤或低級bug,這種情況下最好的思路是直接讓程序崩潰,并修復(fù)代碼。

    和Java不同的是,Rust里沒有異常支持!對于可恢復(fù)異常,Rust使用Result類型來封裝處理結(jié)果,而不可恢復(fù)異常則提供panic!未來終止程序繼續(xù)執(zhí)行。

    不可恢復(fù)異常的支持

    遇到不可恢復(fù)異常的時候,panic!宏會打印錯誤消息(程序員指定),展開線程棧幀,打印出實際出錯的源代碼位置。如果需要打印backtrace信息,則可以在程序運行前設(shè)置環(huán)境變量RUST_BACKTRACE。如果忘記設(shè)置的話,默認的打印輸出會給出溫馨的提示。

    如果不希望展開棧幀而直接暴力終止程序,可以在Cargo.toml中指定

    [profile.release]panic='abort'

    可恢復(fù)異常

    可恢復(fù)異常用一個泛型類Result來傳遞結(jié)果,其定義是

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

    可以使用枚舉類型的模式匹配(見后述) 來優(yōu)雅的解決,譬如這個操作文件的例子

    use std::fs::File;fn main() { let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => { panic!("There was a problem opening the file: {:?}", error) }, };}

    Rust支持一種更簡潔的方法來簡化上述的樣板代碼let f = File::open("hello.txt").unwrap()則返回正常情況下的返回值,如果有異常則直接調(diào)用panic!來終止程序。還有一種更”偷懶/簡潔”的做法是,加上額外的描述字符串 - 大部分情況下出錯了我們總想額外打印一些信息,可以用

    let f = File::open("hello.text").expect("Unable to open file...")

    異常的傳遞和擴散

    這是一個常見的場景,某個API的使用者不想自己去處理異常場景,僅僅想將其傳遞給自己的調(diào)用者去處理,或者程序中有個統(tǒng)一的地方處理異常(通常來說可能不是一個好的主意!)。最基本的思路是,直接將異常返回的類型簽名寫出來,顯示讓調(diào)用者處理。

    下邊這段代碼實現(xiàn)讀入一個文件,從里邊讀取某個字符串,如果成功則返回該字符串,期間有任何錯誤,則傳遞給調(diào)用者。

    fn read_username_from_file() -> Result { let f = File::open("hello.txt"); let mut f = match f { Ok(file) => file, Err(e) => return Err(e), }; let mut s = String::new(); match f.read_to_string(&mut s) { Ok(_) => Ok(s), Err(e) => Err(e), }}

    Rust提供了一種更簡潔的方式(慣用法) - 用"?"操作符來傳遞錯誤,類似的代碼可以重寫為

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

    需要注意到上邊的代碼使用了block的寫法省略return關(guān)鍵字。

    如果追求更精簡的代碼,我們甚至可以用一行代碼來完成上述的函數(shù)體

    let mut s = String::new();File::open("hello.txt")?.read_to_string(&mut s)?;Ok(s)

    是否有種熟悉的函數(shù)式編程的鏈式寫法的味道?

    內(nèi)存訪問模型和并發(fā)

    作為一門面向系統(tǒng)編程的語言,Rust決定了不使用GC,同時基于工程上的原因,讓工程師自己來管理內(nèi)存又顯得不符合時代潮流。Rust采用的策略是讓程序員提供一定的指示給編譯器,然后由編譯器來確保內(nèi)存的分配和訪問總是安全的。

    對于Rust程序用而言,理解堆和棧以及對象的生存期/作用域是必須的,雖然編譯器在后臺做了很多工作。為了支持其內(nèi)存安全和高效約束的目標,Rust提供了一些特殊的語言機制,包括其獨特的對象唯一所有權(quán)的概念和引用語法,其智能指針的概念也比較有特色。

    從語法的角度來看,Rust取消了->操作符,因此所有的方法調(diào)用都是采用obj.doSth()的方式;這點沒什么驚喜,沒有了C的后向兼容負擔,基本上新的語言都是這么干的。在語言層面上,Rust仍然有引用類型的概念;由于要借助編譯器來管理內(nèi)存,Rust的對象作用域規(guī)則有些特殊。

    對象的唯一Ownership

    默認每個對象都是有唯一的所有權(quán)的,這個貫穿在Rust的基本設(shè)計規(guī)則中

  • 任何一個值(基本類型或?qū)ο?都唯一關(guān)聯(lián)一個變量,這個變量被稱為其Owner
  • 任何一個時間點,同一個值僅僅有一個Owner
  • 當其Owner離開作用域的時候(無法被程序再次訪問),值將會被從內(nèi)存中釋放
  • 舉個簡單的例子,當我們聲明let s = "hello world"的時候,字面量"hello world"的Owner就是s本身;當s離開作用域的時候,對應(yīng)的字面量空間就會被釋放。作用域的概念和傳統(tǒng)的C/C++/Java中的很類似,大部分情況下,是通過大括號來限定作用域的。

    比較特殊一點的情況和變量的shadow有關(guān),當一個變量通過shadow的方式重新指向另外一個對象的時候,原來的值因為失去了Owner也應(yīng)該被編譯器悄悄釋放了;當然這里行為仍然是安全的,因為程序沒有通過其它辦法再訪問原來的值。編譯器也可以選擇在真正碰到作用域結(jié)束的時候再釋放,然而這些已經(jīng)屬于編譯器的實現(xiàn)細節(jié)了,應(yīng)用程序無需關(guān)心。非常優(yōu)雅的關(guān)注點分離設(shè)計!

    函數(shù)調(diào)用中的所有權(quán)轉(zhuǎn)移

    和C/C++中不一樣的是,函數(shù)調(diào)用的時候,參數(shù)傳遞會造成所有權(quán)轉(zhuǎn)移即調(diào)用者失去了對原來參數(shù)的所有權(quán)!考慮下邊的例子

    fn main() { let s = String::from("hello"); do_something(s); //s失去對字符串的所有權(quán)! let x = 5; do_somethingElse(x); //內(nèi)置類型被拷貝!}fn do_something(par: String) { //par 擁有外部傳入?yún)?shù)的所有權(quán)} //作用域結(jié)束的時候,par對應(yīng)的對象會被釋放fn do_somethingElse(par: i32) { // play with par}

    上述例子中,當調(diào)用了do_something(s)之后,雖然s還可以訪問但已經(jīng)失去了對應(yīng)對象的所有權(quán),其行為和C++11/14中的Move很像。第二個例子中x對象卻依然可以訪問,這里的不同是,Rust對象對分配在棧上的對象默認采用copy方式處理, 所以僅分配在內(nèi)存堆上的對象被Move,棧上的對象(編譯器必須知道大小)默認是被復(fù)制過去的。

    對于分配于堆上的(大小運行期才知道)對象,Rust也提供了clone方法來(其實是泛型的annotation)執(zhí)行深度拷貝。

    函數(shù)返回的時候,默認也會轉(zhuǎn)移所有權(quán),這點和函數(shù)調(diào)用的參數(shù)傳遞情況類似,只不過是傳遞/接收參數(shù)的順序反了過來,不再詳述。

    引用類型

    如果默認的轉(zhuǎn)移所有權(quán)的方式不符合實際的場景,Rust還提供了引用類型來指示傳遞過程中,僅僅保留對原來參數(shù)的引用而不轉(zhuǎn)移所有權(quán);概念上和C的指針很相像,只是有很多額外的措施避免濫用指針可能出現(xiàn)的空指針、懸掛指針等復(fù)雜問題。

    引用類型在語法上用&符號來表示,可以用于修飾標志符,熟悉C/C++的應(yīng)該不陌生;唯一有點麻煩的是,調(diào)用者和函數(shù)聲明都必須顯示聲明引用類型, 如下邊的例子

    fn calculate_lenght(s: &String) -> usize { s.len()}let s1 = String::from("hello");let len = calculate_length(&s);println!("The length of '{}' is {}", s1, len);

    默認的引用類型是只讀的,因為這個對象是借來的,被調(diào)用函數(shù)沒有所有權(quán);嘗試去修改的話,則會被編譯器報錯攔住。又是一個精妙的設(shè)計,多少粗心的錯誤可以被精明的編譯器攔住。

    可修改的引用和安全性

    如果實在需要在被調(diào)用函數(shù)中修改傳入的引用參數(shù),那么也是可以聲明類型為 &mut SomeType的,只是出于數(shù)據(jù)安全性的考慮(避免可能的運行期錯誤), Rust定義了如下規(guī)則來保證對象的訪問總是安全的;任何可能引起Race Condition的訪問模式都盡量被編譯器攔截住,這樣成功編譯的代碼,出現(xiàn)運行期錯誤的可能性被大大降低了。

  • 引用的對象必須是合法的
  • 同一個作用域內(nèi)(對象是可以被程序訪問到的),可以有多個只讀的引用
  • 同一個作用域內(nèi),如果已經(jīng)有一個可修改引用,那么不允許存在其它任何引用,即使是只讀的也不行
  • 不同的作用域內(nèi),可以有多個可修改的引用;這里因為對對象的修改是相互隔離的,因此不會有意外情況發(fā)生;該規(guī)則能保證程序邏輯正確的同時,又盡可能給上層程序更多的自由度
  • 上述最后一條規(guī)則其實意味著我們可以有意利用它,通過大括號來創(chuàng)建不同的作用域,寫出更簡潔的代碼,比如

    let mut aStr = String::from("hello"){ let r1 = &mut s; //do sth with r1} //r1 離開作用域let r2 = &mut s;//基于r2的修改操作

    另外一種常見的指針錯誤是”懸掛指針”,在傳統(tǒng)的C++程序中,當一個指針指向一個不存在的對象的時候,緊接著所有對指針的操作會導(dǎo)致未定義的行為;由于實際出現(xiàn)錯誤的地方和真正“制造出懸掛指針”的地方可能相距萬里,這類運行期的錯誤往往會耗費程序員大量寶貴的時間。考慮下邊的例子

    fn main() { let reference_to_nothing = dangle();}fn dangle() -> &String { let s = String::from("hello"); &s}

    如果嘗試編譯上述代碼,rust編譯器會清晰的報告一個對象生存期錯誤

    error[E0106]: missing lifetime specifier --> dangle.rs:5:16 | 5 | fn dangle() -> &String { | ^^^^^^^ | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from = help: consider giving it a 'static lifetimeerror: aborting due to previous error

    對象生存期

    在Rust的內(nèi)部實現(xiàn)中,一個隱含的邏輯是,任何一個引用都關(guān)聯(lián)著一個對于的生存期,大部分情況下生存期都可以由編譯器自動推導(dǎo)得到而不需要使用者格外留意。當具體的實現(xiàn)中期望引用的生存期可以根據(jù)某些條件呈現(xiàn)不同的行為的時候,程序員必須提供一些輔助措施告訴編譯器這些額外的判斷信息。

    Rust編譯器內(nèi)部有一個成為BorrowChecker的工具,它在程序編譯的過程中會檢查是否所有的引用是合法的。當它無法判斷引用的生存期的時候,程序員需要在定義的地方傳入一些類似于檢查點的生存期指示幫助編譯器正常檢查。

    考慮一個取2個字符串slice長度最大者并將其返回的一個函數(shù)

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

    編譯這段程序的時候,編譯器就會報錯說,不知道如何決定返回的引用的生存期,因為它要么是x,要么是y, 卻是由程序的運行期的行為來動態(tài)決定的,編譯器沒有辦法在編譯的過程中做決定。修補這個錯誤則需要在函數(shù)簽名中加入生存期標記

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

    這樣編譯器就可以知道其實參數(shù)和返回值的生存期是一致的,不會產(chǎn)生意外的非法訪問或者Race Condition。這里采用的語法是泛型的語法,后邊會詳細考察一下Rust的泛型支持。

    生存期檢查的概念是Rust獨有的,其采用的類泛型的語法學習起來也顯得不是很清晰易懂;這也許是最迷人也最晦澀的特性,從設(shè)計的角度來說, 犧牲一定的簡單性來達到安全編程又不損失性能的目標也許是個不錯的折中; 既想要高層的抽象,又想要極致的性能,還不想有太多意外的錯誤是個刀劍上跳舞的極致挑戰(zhàn),這方面Rust做的很不錯。

    智能指針

    默認的引用方式支持生存期檢查和對象借用,實質(zhì)上采用的仍然是所有者唯一的模型;實際應(yīng)用場景中,程序員可能需要選擇一個可以被多個所有者共享的對象生存期模型, 一如C++中很常用的基于自動引用計數(shù)的shared_ptr的樣子。

    Rust通過標準庫的方式提供了額外的對象生存期管理模型,包括

    • Box類型用于表示一個指向單個堆上分配的對象的指針,該指針的大小在編譯期間是可知的從而我們可以用它來定義遞歸的數(shù)據(jù)結(jié)構(gòu)
    • Deref Trait用于表示一個允許通過解引用來訪問其封裝的數(shù)據(jù)的智能指針
    • RefCell 用來支持可以修改某個不可變參數(shù)內(nèi)部隱藏的數(shù)據(jù)的模式;默認情況下,引用規(guī)則不允許這樣的操作。這種情況下會產(chǎn)生不安全的代碼,需要程序員做一些額外的處理
    • Rc和RefCell用于支持環(huán)形引用而不引入內(nèi)存泄露,這個在GC的算法中很常見

    細節(jié)不一一展開探討,總體上而言智能指針其實是接管了對象的所有權(quán),并且在智能指針內(nèi)部做自動的控制;這一思路現(xiàn)代C++的實踐是英雄所見略同。

    更簡潔的并發(fā)支持

    支持安全而又高效的并發(fā)編程是Rust另外一個雄心勃勃的目標。同時Rust又力圖做到盡可能的簡潔。從語言實現(xiàn)上來說,Rust采用了和控制內(nèi)存安全訪問以及對象所有權(quán)/生存期以及類型系統(tǒng)完全相同的工具來解決并發(fā)的問題, 盡管這些機制看起來和并發(fā)安全相差甚遠。

    經(jīng)由大量的類型系統(tǒng)檢查、對象生存期檢查;大量的并發(fā)編程問題都可以在編譯器被捕獲,從而編譯通過的代碼往往就意味著沒有并發(fā)安全性的問題找上門;程序員可以放心的重構(gòu)其代碼而不用太擔心重構(gòu)后的代碼會破壞并發(fā)安全性;因此Rust稱之為“無所畏懼的并發(fā)”。

    Rust的并發(fā)編程支持一些流行的并發(fā)編程模型

    • 基于消息傳遞的CSP模型,這也是Golang所采用的并發(fā)方式
    • 傳統(tǒng)的基于Mutex和對象的所有權(quán)來控制共享數(shù)據(jù)訪問的方式 - Rust的類型系統(tǒng)和所有權(quán)控制使得其中的挑戰(zhàn)降低了不少

    從設(shè)計上來說,并發(fā)支持不是Rust的核心語言的部分,所有的并發(fā)機制都是通過標準庫來提供的,這也意味著更多擴展的可能;有新的并發(fā)訪問方式,那就寫新的庫唄。

    模塊系統(tǒng)

    編程范式和高級特性

    從編程范式的角度來看,Rust本身其實支持多種編程范式因為其某種程度上對標的是現(xiàn)代的C++或者Golang這樣的競爭對手。

    過程式編程

    傳統(tǒng)的過程式編程風格和基本的C模型比較接近;其定義結(jié)構(gòu)體的方式和C比較類似,依然是采用struct來組織數(shù)據(jù),所不同的是Rust支持“方法”和實現(xiàn)分開定義,通過新的關(guān)鍵字impl來添加新的方法實現(xiàn)。

    考慮一個簡答的例子,定義個矩形以及對應(yīng)的area方法來計算其面積

    struct Rectangle { length: u32, width: u32,}impl Rectangle { fn area(&self) -> u32 { self.length * self.width }}

    這里的area方法綁定于該Rectangle上,第一個參數(shù)總是&self這樣編譯器可以自動推導(dǎo)出其類型是所綁定的struct對象;因為這里的參數(shù)仍然是一個引用, 默認是不能修改結(jié)構(gòu)體的參數(shù),當需要修改時候,可以指定&mut self從而獲取一個可修改的引用。這里引用的生存周期模型仍然是適用的。

    調(diào)用的時候,只需要構(gòu)造一個結(jié)構(gòu)然后,采用structObj.callMethod(...)語法即可;大概是出于簡化語言的考慮,Rust只支持簡單的.語法而丟棄了古老的->操作符; ->的使用僅僅限于指定函數(shù)的返回類型上,干凈清爽了許多。

    let rect = Rectangle { length: 50, width: 30 };println!("The area of rectangle is {}", rect.area())

    Rust也支持類似C++中的靜態(tài)函數(shù)的概念,對應(yīng)的機制Rust稱為關(guān)聯(lián)函數(shù),這樣的機制對大型代碼的組織是很有意義的,可以方便地解決名字沖突的問題。當定義在impl塊里的函數(shù)其參數(shù)中沒有self的時候,Rust會認為其是一個和某個具體的數(shù)據(jù)結(jié)構(gòu)無關(guān)的函數(shù),它和該結(jié)構(gòu)體類在同一個命名空間中。比如我們前邊已經(jīng)看到的String::from("hello")這樣的調(diào)用就是將構(gòu)造方法放置在String的impl塊里,但是完全沒有使用self參數(shù)。

    只是現(xiàn)代的C++社區(qū)因為有更完善的語言層面的命名空間隔離機制,其實已不太推薦這種古老的靜態(tài)函數(shù)組織方式。

    面向?qū)ο蠛头盒途幊?/h3>

    從形式上來說,Rust不提供對傳統(tǒng)的面向?qū)ο缶幊痰闹苯又С?#xff0c;但提供了一些更復(fù)雜的面向接口編程的語言級別機制。這一核心武器就是Rust的Traits。某種程度上說,面向接口編程是面向?qū)ο缶幊套詈诵牡木柚?#xff1b;繼承、封裝和多態(tài)這些基本的武器都可以用面向接口編程的方式來達到。

    Rust的泛型編程實現(xiàn)上有很明顯的C++的影子,不同的是它通過Traits機制巧妙的將編譯器多態(tài)和運行器多態(tài)統(tǒng)一為一體了。

    Traits

    Traits從概念上來說就是接口,它是Rust支持可擴展程序的基礎(chǔ);它既可以支持編譯器多態(tài)(類似于C++的模板元但是比模板元更為簡單一些),也可以支持基于動態(tài)分發(fā)技術(shù)的運行器多態(tài)。從設(shè)計的角度來看,Traits機制受C++的設(shè)計哲學影響比較深,同樣希望達到零成本的抽象這一至高目標

    C++ implementations obey the zero-overhead principle: What you don’t use, you don’t pay for [Stroustrup, 1994]. And further: What you do use, you couldn’t hand code any better.

    Stroustroup

    一個描述Hash函數(shù)的Traits定義如下

    trait Hash { fn hash(&self) -> u64; //can have more functions}

    兩個實現(xiàn)了該Traits的結(jié)構(gòu)可以定于如下(不一定必須放在同一個源代碼文件中)

    impl Hash for bool { fn hash(&self) -> u64 { if *self { 0 } else { 1 } }}impl Hash for i64 { fn hash(&self) -> u64 { self as u64 }}

    和傳統(tǒng)的C++中的抽象類或Java中的接口不同的時候,Traits是半開放的,這意味著我們可以打開某個定義好的結(jié)構(gòu),為其添加新的實現(xiàn);有點類似Ruby的模塊擴展方式。當然Rust 仍然是靜態(tài)語言并且是強類型的。C++的模板元雖然可以達到類似的效果,但只支持編譯器多態(tài),并且其Concept的支持雖然千呼萬喚卻一直沒有進入語言標準。

    基于泛型的編譯器多態(tài)

    考慮一個適用上述Traits的例子

    fn print_hash(t: &T) { println!("The hash is {}", t.hash())}print_hash(&true); // calls with T=boolprint_hash(&12_i64); //calls with T=i64

    這里定義了一個打印Hash的泛型函數(shù)print_hash,要求對應(yīng)的類型必須實現(xiàn)了Hash;實際調(diào)用的時候,編譯器可以做類型檢查來判斷對應(yīng)的實際類型是否滿足Traits約束;和C++的Concept非常相像。

    此外這種類型約束方式還是可以組合的,當期望泛型類滿足多個Traits約束的時候,可以用+將其串起來, 比如 則要求泛型類T必須同時實現(xiàn)Hash和Eq才能編譯通過。

    動態(tài)分發(fā)的運行期多態(tài)

    當多態(tài)行為依賴于具體運行期才精確得知的條件的時候,泛型就無能為力了。Rust的解決方式是,采用額外的中間層-指針來達到。比如在GUI編程中,我們經(jīng)常需要處理界面元素的點擊事件,傳統(tǒng)的面向?qū)ο笏悸肥嵌x一個Traits,然后在具體的界面元素上添加一個事件監(jiān)聽者列表

    trait ClickCallback { fn on_click(&self, x: i64, y: i64);}struct Button { listeners: Vec>;}

    由于結(jié)構(gòu)體的大小必須在編譯期確定,因而直接放一個大小不確定的ClickCallback就不能編譯通過了;標準庫中提供了智能指針來幫我們很優(yōu)雅地解決了這個問題;因為指針的大小總是確定的。具體到實現(xiàn)上,其原理和C++中的虛函數(shù)表非常類似,一個封裝了Traits的智能指針(這里是Box)內(nèi)部結(jié)構(gòu)上類似于一個vtable,其指向一個在運行期動態(tài)構(gòu)造的函數(shù)表。在調(diào)用的地方,編譯器可以自動查找具體實現(xiàn)了對應(yīng)Traits的結(jié)構(gòu)的函數(shù)表,轉(zhuǎn)到正確的調(diào)用地址。

    函數(shù)式編程

    函數(shù)式編程風格具有更高的抽象層次和更豐富的表達能力,更有利于寫出聲明式風格的代碼。較新的編程語言無一例外都或多或少對函數(shù)式編程風格提供支持。Rust的函數(shù)式編程具有明顯的Haskell痕跡

    枚舉類型Enum

    Rust的枚舉類型和傳統(tǒng)的C++/Java中的枚舉的概念類似,都可以用來表示取值有固定可能性的數(shù)據(jù)類型;通過與泛型的結(jié)合,Enum還擁有和Haskell的抽象數(shù)據(jù)類型ADT相同的擴展能力。

    最簡單的枚舉類型定義可以是如下的樣子

    enum IpAddrKind { V4, V6}

    這里每個具體的枚舉值都是一個不同的具體值,同時他們的類型是一樣的。更復(fù)雜一點的情況是,Enum支持每個枚舉的值可以有不同的類型構(gòu)造,如

    enum IpAddr { V4(u8, u8, u8, u8), V6(String),}let home = IpAddr::V4(127, 0, 0, 1)let lo = IpAddr::V6(String::from("::1"))

    更一般地,具體的枚舉值可以用不同的類型來構(gòu)造出來;從而我們由此將不同類型的數(shù)據(jù)聚合在一起形成一個抽象的定義

    struct IpAddr4 { // 細節(jié)省略}struct IpAddr6 { // 細節(jié)省略}enum IpAddr { V4(IpAddr4), V6(IpAddr6)}

    模式匹配

    一個Enum中可能封裝了不同的數(shù)據(jù),當需要對不同的可能的數(shù)據(jù)做不同的處理的時候,Rust采用模式匹配的方式來提高代碼的可讀性。模式匹配是一種特殊的表達式,采用match關(guān)鍵字和一個包含枚舉了所有可能的取值以及其處理代碼的代碼塊組成。譬如考慮上面的地址定義,如果需要對不同的地址類型有不同的處理,可以用模式匹配的方式寫為

    fn handle_address(addr : IpAddr) -> i32 { match addr { IpAddr::V4 => 1, IpAddr::V6 => 2, }}

    這里每一個=>對應(yīng),分隔開,其左邊的部分是某個具體的枚舉變量值,右邊是對應(yīng)的處理表達式。當表達式不止一條語句的時候,可以用大括號隔開。

    模式匹配必須保證所有的枚舉值都必須被處理過;并且處理表達式的類型必須是一樣的;否則編譯器會報錯。當枚舉的可能取值有很多個而處理代碼只對其中部分可能值感興趣,可以用_來表示可以匹配所有之前未匹配到的值。

    另外一種特殊的情況是,我們僅僅關(guān)心某個枚舉值中的一個的時候,match語法依然顯得比較啰嗦;Rust提供了特殊的語法來簡化代碼,如

    let some_u8_value = Some(0u8);match some_u8_value { Some(3) => println!("three!") _ => (),}

    可以改寫為

    if let Some(3) = some_u8_value { println!("three!")}

    類似的我們也可以像常規(guī)的處理一樣加上一個else分支來處理其它不匹配的情況。

    Option類型

    Option是一個封裝類型,其概念和Haskell中的Monad或Java8中的Optional的作用比較類似;都是用于表示一種要么存在一個值要么沒有值的容器。它比空指針有優(yōu)勢的地方在于它是一種應(yīng)用邏輯層的抽象;是用于替代空指針的一個很好的工具。

    I call it my billion-dollar mistake. At that time, I was designing the first comprehensive type system for references in an object-oriented language. My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

    Tony Honare, the inventor of null

    Rust中的Option是一種構(gòu)建于泛型技術(shù)上的特殊的enum類型

    pub enum Option { None, Some(T),}

    標準庫提供了一些成員函數(shù)來實現(xiàn)常見的綁定/鏈式操作范式

    • fn is_some(&self) -> bool 判斷是否有值
    • fn is_none(&self) -> bool 判斷是否為空
    • fn unwrap(self, msg: &str) -> T 用于提取內(nèi)部存儲的值,如果不存在則用給定的消息panic
    • fn unwrap(self) -> T 移動內(nèi)部存儲的值如果其存在的話;不存在則panic
    • fn unwrap_or(self, def: T) -> T 存在的話返回其存儲的值,否則返回提供的默認值
    • fn unwrap_or_else(self, f: F) -> T where F: FnOnce() -> T 嘗試提取值,如果不存在調(diào)用給定的函數(shù)生成一個值
    • fn map(self, f: F) -> Option where F: FnOnce(T) -> U 經(jīng)典的map操作,將值通過給定的函數(shù)轉(zhuǎn)換成另外一個值并封裝成新的Option,如果不存在,則也重新封裝成目標類型的空值
    • fn map_or(self, default: U, f:F) -> U where F: FnOnce(T) -> U 類似于map操作,但返回轉(zhuǎn)換后的類型;如果空則返回給定的默認值
    • fn as_ref(&self) -> Option 返回引用類型
    • fn as_mut(&mut self) -> Option返回可修改的類型
    • fn iter(&self) -> Iter 返回迭代器類型,可以遍歷其值,這里的迭代器總是只能返回一個值
    • fn and(self, optB: Option) -> Option 如果沒有值,則返回空,否則返回給定的新的optB,便于鏈式操作減少邏輯判斷

    閉包Closure

    閉包是另外一個重要的函數(shù)式編程工具;Rust采用的語法是比較類似于Ruby,其內(nèi)部實現(xiàn)上則采用C++的匿名函數(shù)模型;即閉包對象其實生成的是匿名的函數(shù)對象。一個最簡單的例子

    let calculate = |a, b| { let mut result = a * 2; result += b; result};assert_eq!(7, calculate(2, 3)); // 2 * 2 + 3 == 7assert_eq!(13, calculate(4, 5)); // 4 * 2 + 5 == 13

    閉包的類型注解約束要比函數(shù)定義的要求寬松一些,即不需要指定返回類型也可以;和現(xiàn)代C++的generic lambda特性比較類似;都是為了方便程序員寫出更簡潔、干凈的代碼。如下的代碼是完全等價的

    fn add_one_v1 (x: i32) -> i32 { x + 1 } // a functionlet add_one_v2 = |x: i32| -> i32 { x + 1 }; // the full syntax for a closurelet add_one_v3 = |x| { x + 1 }; // a closure eliding typeslet add_one_v4 = |x| x + 1 ; // without braces

    從代碼可讀性和可維護性的角度來看,最好不用閉包來寫太長/太復(fù)雜的代碼塊, 因為隨著匿名代碼塊中邏輯的增加,上下文邏輯變得更加模糊;這個時候,用一個命名良好的子函數(shù)反而更清晰便于維護。

    軟件工程支持 - 工具和方法

    Rust提供了成熟的軟件工程實踐支持;有相對完善的模塊文檔和官方的gitboook。

    Creates && Cargo系統(tǒng)

    作為一門站在巨人肩上的語言,Rust吸收了已有的一些成熟的包管理系統(tǒng)的經(jīng)驗,并提供了類似的機制來支持更好的協(xié)作

    • Creates和其包分發(fā)系統(tǒng)有點Hackage的影子,又有點NPM的味道
    • 版本依賴管理上,和Ruby Gems的處理方式也有些像,雖然toml的格式?jīng)]有Ruby的DSL那么靈活強大

    Cargo是一個類似于C++中的CMake的系統(tǒng),同時還提供了一些創(chuàng)建項目模板的快捷方式,幫助程序員快速創(chuàng)建項目骨架,更快專注于具體的實現(xiàn)而不是構(gòu)建細節(jié)。可以用它的子命令來

    • 檢查依賴,自動升級依賴
    • 增量編譯代碼并報告錯誤
    • 根據(jù)特定的開關(guān)選項執(zhí)行對應(yīng)的測試
    • 生成文檔
    • 運行項目生成的可執(zhí)行文件(如果不是編譯一個庫)
    • 運行benchmark
    • 安裝編譯好的二進制構(gòu)建結(jié)果
    • 搜索crates中注冊的模塊

    具體功能可以查看其命令行幫助。

    IDE和編輯器插件支持

    某些程序員更喜歡IDE,另外一些人則更熟悉命令行的Vim/Emacs或者其它輕量級的編輯器。社區(qū)目前提供了比較豐富的支持,包括對Eclipse/IntelliJ/Visual Studio的IDE插件, 以及對Atom/Visual Studio Code/Sublime/Vim/Emacs的插件支持;基本上比較主流的編程環(huán)境的支持都有了;具體支持程度如何,有待進一步驗證;官方的文檔看起來非常值得一試。

    測試

    Rust支持在包中提供單元測試和功能測試。默認的工具會搜索源碼目錄中的所有單元測試,并自動組織起來運行,同時也提供了一些高級的測試支持。Rust希望程序員明確的區(qū)分這兩種測試,并采用不同的約定

    • 所有的單元測試都和被測試的源代碼放在一起,并且支持對private方法的測試(當然這個很有爭議,個人建議不要測試private)
    • 集成測試被放在專門的test文件夾下邊,可以放在多個文件中,Cargo將會為每個文件生成一個crates

    cargo test命令可用來執(zhí)行所有的測試,并且默認是并發(fā)執(zhí)行的;這樣開發(fā)的反饋周期會更短;也可以用命令來顯示要求線性執(zhí)行 - 傳入 --test-threads=1即可。一些更復(fù)雜的特性,如指定某個case的執(zhí)行,跳過某些特定的case,以及按照某個過濾條件來選擇特定的case,忽略case運行過程中的打印輸出等特性也被貼心的支持了。

    總結(jié)

    在注重極致性能又強調(diào)工程協(xié)作和擴展性的系統(tǒng)編程領(lǐng)域,Rust做了比較大膽的嘗試,在不引入垃圾收集并保持強類型檢查的前提下, 它期望能將C++的零成本抽象推向一個新的高度而又能避免陷入傳統(tǒng)C/C++語言指針訪問安全性以及復(fù)雜的模板元編程等復(fù)雜性的泥潭。

    它的泛型編程支持和強調(diào)值對象唯一所有權(quán)的概念和對象生存周期的強制檢查使得多線程并發(fā)編程變得輕松簡單;加上強類型檢查的約束,編譯通過的程序往往運行期錯誤也變得很少,這一來自于Haskell的設(shè)計哲學深深地影響著Rust。

    從一開始就加入的包管理器機制和對豐富的軟件工程工具支持以及對開源社區(qū)的熱情擁抱,使得Rust一開始就汲取了傳統(tǒng)C/C++語言工程化支持不足的一些教訓(xùn)。中心化的軟件倉庫以及對流行IDE、編輯器環(huán)境的支持使得它可以更好地贏得社區(qū)的支持。

    與此同時隨著更新節(jié)奏的加快,基于ISO標準化的C++語言也在通過更快的迭代速度和更短的更新周期對這些新加入的競爭者予以反擊;期望Rust能在系統(tǒng)編程領(lǐng)域掀起新的波瀾。

    總結(jié)

    以上是生活随笔為你收集整理的rust为什么显示不了国服_Rust编程语言初探的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 亚洲欧洲日本一区二区三区 | 国产3p精品一区 | 狠狠躁日日躁 | 中文亚洲av片不卡在线观看 | 人妻洗澡被强公日日澡电影 | 久久黑人 | 国精产品一品二品国精品69xx | 日韩欧美激情视频 | 偷拍第1页 | 极品国产在线 | 在线观看av大片 | 无码无套少妇毛多18pxxxx | 亚洲精品亚洲人成人网 | 国产一级片麻豆 | 亚洲色图丝袜美腿 | 成年午夜视频 | 亚洲熟女乱色综合亚洲小说 | 日韩有码中文字幕在线观看 | 色多多视频在线 | 影音先锋男人站 | 欧美xxxxbbbb| 国产精品无码电影 | 欧美91看片特黄aaaa | 日韩av在线免费 | 成人免费看片98欧美 | 免费毛片在线播放免费 | 吊视频一区二区三区 | av色图在线| 污网在线观看 | а√天堂中文在线资源8 | 久操福利| 在线视频毛片 | 狠狠91| 香蕉综合网 | 国产精品免费一区 | 美国黄色av | 日本a一级 | 一级片在线观看免费 | 日本美女视频网站 | 涩涩涩涩av | a级在线免费观看 | 新亚洲天堂 | 欧美视频三区 | 日本我不卡| 99精品一级欧美片免费播放 | 欧美少妇性生活 | 五月婷婷丁香激情 | 91丝袜国产在线观看 | 亚洲国产精品综合久久久 | 欧美亚洲色图视频 | 人人爽爽人人 | 免费在线观看你懂的 | 中文字幕在线观看日韩 | 欧美mv日韩mv国产网站 | 日本成人三级电影 | 日韩精品一区二区三区四区五区 | 亚洲综合视频在线播放 | 香蕉网在线视频 | 欧美视频导航 | 96亚洲精品久久久蜜桃 | 国产在线观看免费视频今夜 | 蜜桃久久精品 | 好吊视频一区二区三区 | 亚洲熟女综合色一区二区三区 | 亚洲国产aaa| 老牛影视一区二区三区 | 欧美在线观看一区 | 97自拍偷拍| 欧美日韩国产传媒 | 亚洲乱色熟女一区二区 | 人人人人爽 | 自拍偷拍20p| 永久免费毛片 | 四虎影视最新网址 | 成人福利影院 | 色网站在线观看 | 深夜视频在线观看免费 | 天堂网中文字幕 | 精品久久人人妻人人做人人 | 女~淫辱の触手3d动漫 | 黄色一级大片在线免费看国产 | 中国久久久 | 国产又粗又猛又大爽 | 少妇高潮一69aⅹ | 天天干天天要 | av资源网站 | 亚洲v日韩v综合v精品v | 久草99| 狠狠艹视频 | 午夜影院18 | 欧美一区二区三区啪啪 | 超碰999 | 麻豆射区 | 少妇熟女一区二区 | 欧美日韩成人 | 国产精品国产三级国产三级人妇 | 仙踪林久久久久久久999 | 亚洲精品乱码久久久久久自慰 | 成人av电影免费观看 |