一文學會Rust

2022-03-28 12:05:57 字數 3844 閱讀 8969

rust是什麼

rust 是乙個系統程式語言,它注重三個方面:安全,速度和併發性。

特徵:1.沒有垃圾**機制,沒有執行時,效率超過c++,直逼c語言

2.記憶體安全,併發安全,沒有空指標

3.極其豐富的生態  ,

它是如何做到這些的?

編譯時保證對記憶體生命週期的明確控制

讓我們來談談rust中最重要的概念:「所有權」,以及它對併發程式設計(對程式設計師來講通常是非常困難的任務)的啟發。

所有權

所有權是rust的核心概念,也是其獨特的功能之一。 「所有權」是指允許哪部分的**修改記憶體。

讓我們從一些c++**開始理解這個概念:

int *foo(void) 

int bar(void

)

foo函式在棧上分配了乙個整型,然後儲存給乙個變數i,最後返回了這個變數i的引用。這裡有乙個問題:當函式返回時棧記憶體變成失效。意味著在函式add_one第二行,指標num指向了垃圾值,我們將無法得到想要的結果。雖然這個乙個簡單的例子,但是在c++的**裡會經常發生。當堆上的記憶體使用malloc(或new)分配,然後使用free(或delete)釋放時,會出現類似的問題,但是您的**會嘗試使用指向該記憶體的指標執行某些操作。 更現代的c ++使用raii和建構函式/析構函式,但它們無法完全避免「野指標」。

rust就可以不出現這種情況。 我們試試吧:

fn foo() -> &i8

fn bar() -> i8

當你嘗試編譯這個程式時,你會得到乙個有意思的錯誤資訊:

rustc main.rs

error[e0106]: missing lifetime specifier

--> main.rs:1:13

|1 | fn foo() -> &i8 );

}for _ in

0..10

", numbers[0]);}}

在這個例子中,我們建立了乙個數字陣列(vector), 然後我們建立乙個channel,這是rust實現通道的方法,這將返回通道的兩個不同端:傳送端(tx)和接收端(rx)。spawn函式可以啟動乙個task。 正如你在**中看到的那樣,我們在task中呼叫tx.send(),傳入vector,我們在外面呼叫rx.recv()。 然後列印vector的第乙個元素。

rust在通過channel傳送前copy了資料, 這樣即使它傳送的資料是可變的,由於每個執行緒有自己的乙份資料,就不會出現競爭。怎麼來證明這裡的物件copy了呢?我們修改一下**:

use std::thread;

use std::sync::mpsc;

fn main() );

}for _ in

0..10

", numbers);

}}

注意看紅字的部分,如果這個numbers是同乙份物件,那麼在累加之後列印的結果應該是一直遞增的數,如果是多份,那麼輸出的結果應該是從1到10

[2, 2, 3]

[1, 2, 3]

[4, 2, 3]

[5, 2, 3]

[3, 2, 3]

[6, 2, 3]

[9, 2, 3]

[10, 2, 3]

[8, 2, 3]

[7, 2, 3]

輸出結果可以證明channel傳輸的每個物件都是copy的。如果我們啟動了很多task,同時我們的資料又非常大,那麼為每個task都copy副本會使我們的記憶體使用量膨脹而沒有任何實際好處。

你可能注意到了:我們並沒有為每乙個執行緒建立乙個channel,tx物件和rx物件都只有乙個,為什麼可以給多執行緒使用。我們使用了tx.clone()獲得了tx的引用。(當我們需要一些型別可以讓我們擁有乙個值的多個有所有權的引用。我們使用rc,它是乙個引用計數型別用以提供共享的所有權。它有一些執行時記錄來跟蹤引用它的數量,也就是「引用計數」。呼叫rcclone()為了解決這個問題,我們使用arc,rust 標準的原子引用計數型別。

arc是「原子引用計數」的縮寫,它是一種在多個task之間共享不可變資料的方法。我們也可以像tx物件那樣將上面的**改進為引用計數的形式:

use std::thread;

use std::sync::arc;

use std::sync::mpsc;

fn main() );

}for _ in

0..10

", local_arc);

}}

這時rust就不會copy物件了。這與我們之前的**非常相似,除了現在我們迴圈10次,啟動10個task,並在它們之間傳送乙個arc。 arc :: new建立乙個新的arc,.clone()返回arc的新的引用。 因此,我們為每個task建立乙個新的引用,將該引用傳送到通道,然後使用引用列印出乙個數字。 現在我們不copy vector。

arc非常適合不可變資料,但可變資料呢?共享變數是併發程式的禍根,其實我們應該使用執行緒間通訊,能摒棄的都摒棄,如果你的變數是簡單競爭的,不想使用通訊,才考慮使用鎖。

您可以使用互斥鎖(mutex)來保護共享的可變狀態,**這樣修改:

use std::thread;

use std::sync::;

use std::sync::mpsc;

fn main() );

}for _ in

0..10

", local_arc);

}}

互斥鎖太簡單粗暴了,rust為共享可變狀態還提供了乙個工具:rwlock。讀寫鎖,它的特點是:同時允許多個讀,最多只能有乙個寫;讀和寫不能同時存在;

rwlock的api與mutex略有不同:有read和write方法允許您讀取和寫入資料。 這兩個方法都將閉包作為引數,並且在寫入的情況下,rwlock將獲取互斥鎖,然後將資料傳遞給此閉包。 閉包完成後,互斥鎖被釋放。

你可以看到rust在你不記得獲取鎖的情況下是不可能讓你改變共享變數的。 我們獲得了共享可變狀態的便利,同時保持不允許共享可變狀態的安全性。

unsafe

雖然rust編譯器非常聰明,並且可以避免你通常犯的錯誤,但它不是人工智慧,我們比編譯器更聰明,在某些**場景中,我們需要打破這種安全檢查。 為此,rust有乙個unsafe關鍵字。 在乙個unsafe的**塊裡,rust關閉了許多安全檢查。 如果您的程式出現問題,您只需要審核您在不安全範圍內所做的事情,而不是整個程式。

如果rust的主要目標之一是安全,為什麼要關閉安全? 嗯,實際上只有三個主要原因:與外部**連線,例如將ffi寫入c庫;效能,(在某些情況下)圍繞通常不安全的操作提供安全抽象。 我們的arcs就是後乙個目的的而使用unsafe關鍵字。

我們可以安全地分發對arc的多個引用,因為我們確信資料是不可變的,因此可以安全地共享。 我們可以分發對rwarc的多個引用,因為我們知道我們已經將資料報裝在互斥鎖中,因此可以安全地共享。

但rust編譯器無法知道我們已經做出了這些處理,所以在arc/rwarc的實現中,我們使用unsafe的塊來做了(通常來說)一些危險的事情。但是我們暴露了乙個安全的介面,這意味著arc/rwarc不可能被錯誤地使用。

這就是rust的型別系統如何讓你不會犯一些使併發程式設計變得不可控的錯誤,同時也能獲得像c++等語言一樣的效率。

一文學會sass常用語法

專案一直用的scss,但是一直沒有好好學些sass的用法,一直用的只有定義變數和選擇器巢狀等寥寥幾個功能 捂臉 後來仔細看了一下sass中文網,感覺自己錯過了乙個世界 2.1 屬性巢狀 帶有相同字首的css屬性可以用巢狀的規則書寫 text item text 也可以給字首本身新增屬性 text c...

一文學會python正規表示式

在程式設計中,經常會涉及到字串的操作,乙個常用的策略就是利用split函式,然後對於特定的字串進行匹配,但是這種方法格式複雜,可復用性較差。正規表示式是處理字串匹配乙個必不可少的方法,定義乙個語義規則,來進行特定的字元字串的規則。在程式設計中,經常會涉及到字串的操作,乙個常用的策略就是利用split...

一文學會磁碟分割槽 格式化 永久掛載

分割槽格式 fdisk 分割槽裝置路徑 互動指令 m 列出幫助 p 檢視現有的分割槽 n 建立新分割槽 d 刪除分割槽 q 放棄更改並退出 w 儲存並退出 強制重新整理分割槽 partprobe root localhost lsblk 檢視新增的磁碟 name maj min rm size ro...