Rust教學:記憶體安全與並發性

最後更新: 04/12/2025
作者: 艾薩克
  • Rust 透過所有權、借用和生命週期在編譯時確保記憶體安全,而無需使用垃圾回收。
  • 類型系統和別名規則允許使用互斥鎖、通道和智慧指標實現並發而不會出現資料競爭。
  • Cargo、crates.io 和活躍的生態系統簡化了依賴管理、編譯、測試和部署。
  • 理解結構體、枚舉、Option 和 Result 是處理並發應用程式中的錯誤和建模安全資料的關鍵。

C語言與Rust語言:優缺點分析

Rust 已經成為系統開發人員耳熟能詳的語言之一:它像 C 和 C++ 一樣快速,但對記憶體安全和高效並發執行卻有著近乎執著的關注。這並非空洞的行銷噱頭:它的設計核心在於編譯器在編譯時會偵測錯誤——而這些錯誤在其他語言中只有在系統投入生產環境或崩潰時才會被發現。

如果您想了解Rust 如何在不使用垃圾回收的情況下實現記憶體安全,以及如何在避免資料競爭的情況下實現並發,那麼本教學正是為您準備的。我們將涵蓋從語言及其生態系統的基礎知識到所有權、借用、複合類型等關鍵概念,以及 Cargo 等工具,甚至還會以更易於理解的方式介紹原子性和鎖機制,方便並發編程新手入門。所有內容都將圍繞安全性和效能。

Rust教程:效能、記憶體安全和並發性

Rust 是一種通用型、多範式程式語言,既適用於底層系統編程,也適用於高階項目,其應用範圍涵蓋作業系統、遊戲引擎、瀏覽器以及高效能 Web 服務。它最初由 Mozilla 開發,旨在提高軟體安全性,尤其是在瀏覽器引擎等敏感元件方面。

Rust 的主要特點在於它無需垃圾回收器即可在編譯時確保記憶體安全。 Rust 採用所有權系統和借用檢查器來追蹤每個值及其引用的生命週期。這可以防止懸空指標、緩衝區溢位和記憶體洩漏等常見問題,而無需自動引用計數或垃圾回收。

此外,Rust 的設計旨在實現安全的並發。它的類型和所有權模型可以防止線程間的資料競爭,至少在安全的 Rust 程式碼範圍內是如此。這意味著許多危險情況會在編譯時被偵測到,甚至在執行任何一行程式碼之前就能發現。

鑑於上述種種原因,Dropbox、微軟、亞馬遜和谷歌等大型公司已在其基礎設施的關鍵部分採用了 Rust。 Rust 多年來一直穩居 Stack Overflow 開發者最愛語言榜首,這絕非偶然:它兼具 C++ 式的高效能、現代化的工具集(Cargo、crates.io)以及一個非常活躍的社群——Rustaceans。

基本概念:程式語言、型別與內存

在深入探討記憶體安全和並發的具體細節之前,有必要澄清一些在使用 Rust 時經常遇到的通用概念,特別是如果你來自其他語言或剛開始程式設計的話。

程式語言歸根究底是… 一套規則和結構,用來描述演算法 並將它們轉換為可執行程式。 Rust 使用其編譯器編譯成本地機器碼。 rustc因此,其性能通常與 C 和 C++ 相當。

記憶體管理是指程式在運行時分配和釋放記憶體區塊的過程。這方面的錯誤通常是致命的:例如記憶體洩漏(未能釋放未使用的記憶體)、越界寫入導致的資料損壞,或在記憶體釋放後繼續使用。 Rust 透過非常強大的類型系統以及所有權、借用和生命週期的正式規則來解決這個問題。

Rust 也包含諸如此類的術語 智慧型類型和指針類型描述了變數儲存的資料類型(整數、浮點數、字串、結構體等)以及如何操作這些資料。智慧指針(例如, Box, Rc y Arc) 是封裝記憶體位址並添加額外邏輯以安全地管理資源的結構,例如計算共享參考或將值移至堆中。

在並發領域,競爭條件、互斥鎖和通道等概念變得至關重要:競爭條件是指多個執行緒在沒有適當協調的情況下同時存取和修改共享資源;互斥鎖(互斥)確保一次只有一個執行緒進入臨界區;通道允許在執行緒之間發送訊息,而無需直接共享記憶體。

為什麼要學習 Rust:記憶體安全和無畏的並發性

Rust之所以享有盛譽,是因為它為現代程式設計提供了三大支柱:效能、安全性和最新的工具。讓我們來探討為什麼這些方面如此重要。

在效能方面,Rust可以直接編譯成原生二進位文件,無需虛擬機器或解釋器。其零成本抽像模型旨在確保高級抽像不會在運行時增加額外開銷。因此,它非常適合開發系統、視訊遊戲、瀏覽器組件或低延遲微服務。

記憶體安全依賴其所有權和借用機制。雖然沒有垃圾回收器,但編譯器能夠精確地知道每個資源的歸屬、何時不再需要以及何時可以釋放。這有效防止了記憶體洩漏、懸空指標以及許多其他傳統上導致 C 和 C++ 程式設計風險極高的錯誤。

在競爭領域,Rust 追求的通常是所謂的 “無所畏懼的並發”類型系統本身就阻止了資料根存在於安全代碼中。如果要在線程之間共享可變數據,則需要使用適當的原語,例如: Mutex, RwLock o Arc編譯器將確保別名和可變性規則得到遵守。

  如何選擇一家好的3D列印服務商而不出錯

現代化的工具,例如整合的套件管理器和建置基礎架構Cargo,以及涵蓋從非同步網路(Tokyo)到 Web 框架(Actix、Rocket、Axum)等各個領域的廣泛程式庫(crate),大大提升了開發體驗。這一切都得益於一個開放、活躍且極具耐心(尤其對初學者而言)的社群的支持。

安裝和必備工具:rustup、rustc 和 Cargo

要用 Rust 編寫和運行你的第一個程序,通常的做法是先使用rustup安裝官方工具鏈(參見Rust 的完整介紹),rustup 是一個簡單的安裝程序和版本管理器,可在主流作業系統上運行。

同 生鏽 您可以安裝、更新和切換不同版本的 Rust(穩定版、測試版、每日建置版),而不會破壞任何現有功能。只需造訪 Rust 官方工具頁面,並按照適用於您系統的步驟操作即可。安裝完成後,編譯器即可使用。 rustc專案經理 cargo 和他自己的 rustup 在你的 終端.

編譯器 rustc 它將你的原始程式碼轉換成可執行的二進位檔案或函式庫。雖然你也可以直接呼叫它。 命令 如 rustc main.rs實際上,你幾乎總是會透過 Cargo 來完成工作,它負責處理對 Cargo 的呼叫。 rustc 選擇正確的選項。

工作流程的核心工具是 貨物只需幾個命令,您就可以在 crates.io 上建立新專案、管理依賴項、編譯、運行、測試和發佈軟體包。一些常用的基本指令包括: cargo new, cargo build, cargo run, cargo test y cargo check它可以檢查程式碼而不產生最終可執行文件,非常適合快速檢測錯誤。

如果你想在不安裝任何東西的情況下進行嘗試,Rust Playground(官方在線執行器)和 Replit 等平台允許你從瀏覽器中編寫和運行小段代碼,非常適合試驗內存和並發示例,而無需設置整個環境。

你的第一個程式:Hello、Rust 和基本流程

在任何語言中,開啟對話的經典方式都是使用著名的「Hello, world」。在 Rust 中,一個文件 main.rs 最小值可以包含像函數這樣簡單的內容。 main 它會在螢幕上列印一個字串。.

關鍵字 fn 這表明我們正在定義一個函數,並且 main 這是程序的入口點。函數的程式碼區塊放在花括號內。若要向控制台寫入內容,請使用… 宏 println!它接受一個字串字面量(或帶有書籤的模板),並將其發送到標準輸出,以換行符結尾。

如果你直接編譯 rustc main.rs您將獲得一個可執行二進位(例如, main o main.exe (取決於系統)。運行後,您會在終端機看到相關資訊。但使用 Rust 的慣用方法是讓 Cargo 主導專案。

同 cargo new nombre_proyecto 系統會自動建立資料夾結構。 src/main.rs 已經準備好了「Hello, world」和一份文件 Cargo.toml 其中包含元資料和未來依賴項。從那裡開始, cargo run 編譯並執行二進位文件只有在偵測到變更時才會重新編譯。

這種工作方式不僅方便,而且讓你從一開始就習慣使用標準的 Rust 生態系統,這在你開始添加用於並發、網絡、測試或任何你需要的 crate 時非常有用。

// 我們宣告主函數:程式入口點 fn main() { // 我們使用 println! 巨集將文字印到控制台 println!("Hello, world!"); }

變數、可變性和基本資料類型

在 Rust 中,變數使用關鍵字聲明。 let預設情況下 不可改變換句話說,一旦你給它們賦值,除非你明確地將它們聲明為可變的,否則你不能修改它們。 mut.

預設情況下,不可變性有助於避免一些細微的邏輯錯誤,尤其是在並發程式中,多個執行緒可能想要修改同一個值。如果需要修改,你可以這樣寫: let mut contador = 0;從那裡你可以重新分配新值 contador.

Rust 也允許所謂的 影子你可以在同一作用域內宣告一個同名的新變量,從而隱藏先前的變數。這與修改變數不同,因為你正在創建一個新值(甚至可以是不同的類型)。例如,你可以使用相同的名稱將字串轉換為整數,只要它是新的宣告即可。 let.

Rust 的型別系統是靜態的,這意味著 每個變數的類型在編譯時就已經確定了。然而,類型推斷非常強大:如果你寫 let x = 5;編譯器假定它是一個 i32 除非您另行說明。您可以新增備註,例如: let x: i64 = 5; 當你想明確表達的時候。

可用的標量類型包括有符號整數和無符號整數(i8, u8, i32等等),漂浮的那些(f32, f64),布爾(bool)和 Unicode 字元(char). 這些簡單類型通常複製成本很低,而且許多類型都實現了該特性。 Copy這意味著,當你將它們賦值或傳遞給函數時,它們會被複製而不是移動。

Rust 中的字串:&str 和 String

Rust 中的文字處理一開始可能會讓人有點困惑,因為它清楚地區分了… 連鎖「切片」和專有連鎖這兩個關鍵部分是 &str y String.

Un &str 是 不可變鏈的片段儲存在某處的 UTF-8 位元組序列的視圖。典型範例包括字面量,例如: "Hola"它們是以下類型的 &'static str (它們在程式的整個生命週期內都存在,並嵌入在二進位檔案中。)切片並不擁有資料;它們只是指向資料。

String另一方面,是 自訂字串,可變且託管在堆中。它可以調整大小、連接、透過移動其屬性在函數之間傳遞等等。它常用於建立動態文字或將其長期儲存在結構中。

  在 iPhone 或 iPad 上禁用 WiFi 和藍牙的簡單方法

在許多情況下,你會在兩者之間進行轉換:例如,你會創建一個 String::from("hola") 切片或者你會借用一個 &str 一 String 透過傳遞對只需要讀取資料的函數的參考。

擁有資料和借用資料之間的這種分離是記憶體管理的關鍵,並且擴展到了語言的其他部分:集合、結構和枚舉都遵循相同的理念,即誰擁有誰只是查看。

函數、控制流和註釋

Rust 中的函數是用以下方式定義的: fn 並允許將程式組織成可重複使用的邏輯單元。每個函數都指定 它的參數類型和返回類型 沿著箭頭 ->如果未傳回任何有意義的結果,則假定為單一類型。 ().

一個重要的細節是,函數(或任何程式碼區塊)中最後一個沒有分號的表達式將被視為隱含回傳值。你可以使用 return 儘早獲得回報但在慣用的程式碼中,通常只需省略最後一個表達式即可。 ;.

控制流採用經典方式處理。 if/else循環 loop, while y for在 Rust 中, if 這是一個傳回值的表達式所以你可以直接在…中使用它 let前提是分支回傳的類型相同。循環 for 它們通常遍歷範圍或集合迭代器,是比手動索引更推薦的選擇。

為了記錄代碼並方便後來者(包括一個月後的自己)使用,你可以使用 帶有行註解的 // 或阻止 /* ... */此外,Rust 還提供了文件註釋功能。 /// 這些文件會生成,但這更適合大型專案。

所有權、借貸與生命週期:記憶安全的基礎

至此,我們便觸及了Rust記憶體模型的核心:所有權、借用和生命週期系統。這些規則確保引用始終有效,並且記憶體能夠安全釋放,不會累積垃圾。

所有權的基本規則雖然簡單易懂,但起初可能難以理解和接受: 每個值都只有一個擁有者。同一時間只能有一個所有者;當所有者離開其作用域時,該值將被銷毀並釋放其記憶體。例如,這適用於… String當聲明它的程式碼區塊執行完畢後,它會自動被呼叫。 drop 這樣就能釋放堆記憶體。

當你給另一個變數賦值或將其按值傳遞給函數時,該屬性會被移動。這意味著移動後,原始變數將不再有效。這種移動語義可以防止重複釋放,因為永遠不會有兩個所有者試圖釋放同一個資源。

為了允許程式中的多個部分存取同一個值而不改變所有權,Rust 引入了引用和借用機制。借用時,你會建立一個引用。 &T (不可變的)或 &mut T (可變)在不轉移所有權的情況下改變值。 貸款金額受貸款審核機構規則的限制。它會檢查引用是否比它們指向的資料更持久,以及可變存取和共用存取是否危險地混合在一起。

借用規則可以概括如下:在任何給定時間,你只能擁有對同一值的多個不可變引用,或者一個可變引用,但不能同時擁有兩者。這消除了共享記憶體中的競態條件:要麼有多個讀者,要麼只有一個寫者;絕不可能同時存在多個讀者和寫者存取同一資料。

複合型別:結構體、枚舉和智慧指針

Rust 提供了多種將相關資料分組到更豐富的結構中的方法,首先是結構體(struct)。結構體可讓您定義具有命名欄位的自訂類型,例如,一個包含電子郵件、姓名、活動狀態和登入計數器的使用者。

要建立結構體實例,需要填寫所有字段,並且可以將包含該結構體的變數標記為可變,以便稍後修改其值。此外,還有結構體更新語法,可讓您透過重複使用現有實例中的某些欄位來建立新實例。 ..otro_struct.

很多 列舉 它們是另一個重要的支柱:它們允許您定義一個類型,該類型可以是多個可能的變體之一,每個變體都可以帶有或不帶有關聯資料。一個經典的例子是 IP 位址枚舉,它只有一個變體。 V4 它儲存四個八位元組和另一個 V6 儲存一個包含 IPv6 表示法的字串。

Rust 的標準函式庫包含兩個非常重要的枚舉: Option<T> y Result<T, E>第一個表示值的存在與否(有或無),用於避免空指標;第二個表示可以執行的操作。 傳回正確結果或錯誤要求錯誤處理必須明確且安全。

為了管理動態記憶體和共享數據,Rust 擁有 智慧指針 如 Box<T>將值移至堆並保持唯一所有權; Rc<T>,單線程環境下的共享引用計數;以及 Arc<T>,類似於 Rc 但它對多線程安全。在將動態記憶體與並發結合使用時,正確使用它們至關重要。

貨物和貨箱生態系統

Cargo 是將 Rust 生態系統維繫在一起的黏合劑: 管理編譯、相依性和專案生命週期每個項目都有一個文件 Cargo.toml 它充當清單,聲明名稱、版本、語言版本和外部依賴項。

部分 此文件允許您列出第三方庫及其版本。當您運行 cargo build o cargo runCargo 會自動從 crates.io 下載這些 crate,編譯它們,並將它們連結到你的專案中。增加隨機數產生器、Web 框架或加密庫就是這麼簡單。

  如何將條件格式應用於Word表格:提示和範例

最常用的命令包括: cargo new 啟動二進位項目 o cargo new --lib 適用於圖書館; cargo build 以調試模式編譯; cargo build --release 獲得最佳化的、面向生產的版本; cargo test 運行一系列測試。

cargo check 它值得特別一提:它將程式碼編譯到中間狀態,而不產生二進位文件,這使得它 能夠非常快速地檢測編譯錯誤它非常適合快速迭代,同時借用檢查器會指出屬性、引用和生命週期方面的問題。

由於這種生態系統,將專案建構成小型、定義明確的 crate 已成為一種常見的做法,這樣可以在 crate 之間共享程式碼並重複使用社群創建的解決方案。例如,對於高級並發編程,可以使用 Tokio 這樣的 crate 進行非同步編程,或使用 crossbeam 來實現高效能並發資料結構。

Rust 中的並發:線程、互斥鎖、通道和原子操作

並發性是 Rust 備受關注的原因之一:它允許你充分利用多核心處理器,而不會陷入線程和共享記憶體等常見陷阱。如果你是第一次接觸這些主題,區分幾個概念會很有幫助。

並發是指在單一或多個核心上執行多個時間上重疊的任務。在 Rust 中,你可以建立系統執行緒來並行執行工作,語言會引導你確保執行緒間的資料共享是安全的。一個典型的錯誤是競態條件,即兩個執行緒同時存取和修改數據,而結果取決於執行順序——這種情況非常難以調試。

為了協調對共享資料的訪問​​,Rust 依賴諸如以下的基本函數: 互斥鎖它保證了互斥性:一次只能有一個執行緒進入臨界區。結合 Arc<T> 為了在執行緒之間共享所有權,可以建立符合所有權和借用規則的共享資料結​​構。

Rust 中大力提倡的另一種常見的線程間通訊方式是使用通道(channel)進行訊息傳遞。通道有發送端和接收端;執行緒透過通道傳遞訊息(值),從而減少對可變共享記憶體的使用,並簡化對系統狀態的判斷。

深入研究底層並發時,原子型就會浮現出來。這些變數透過線程層面上不可分割的操作進行存取。這使得共享計數器、狀態標誌、無鎖隊列等功能的實現成為可能。掌握原子類型需要理解記憶體模型和存取命令,因此許多開發者傾向於先從互斥鎖和通道入手,然後再深入研究這些細節。

學習並發和原子操作的入門知識和資源

如果你之前沒有任何經驗,那麼在學習 Rust 的原子類型等進階工具之前,最好先打好基礎,掌握一些基本概念。像《Programming Rust》這樣的書可以提供循序漸進的介紹,但專注於原子類型和鎖的書籍一開始看起來比較晦澀難懂也是正常的。

為了更方便起見,建議您先熟悉以下內容: 傳統線程、互斥和訊息傳遞 在 Rust 中。嘗試一些範例。 std::thread, std::sync::Mutex, std::sync::Arc 和渠道 std::sync::mpsc 它能幫助你了解編譯器如何引導你以及它避免了哪些錯誤。

同時,強烈建議複習一些關於並發程式設計的入門資料,即使它們並非專門針對 Rust:理解什麼是競態條件、阻塞的含義、共享記憶體與訊息傳遞的區別,以及鎖的用法。一旦這些概念成為你的本能,原子鎖就不再是「黑魔法」,而只是眾多工具中的一個,儘管它非常精細。

當你回到 Rust 中關於原子操作和鎖的更高級文本時,如果你已經理解了每個構造試圖解決的問題,那麼理解其中的推理就會容易得多:從簡單的線程安全計數器到最大限度減少爭用的無鎖結構。

歸根結底,Rust 既提供了高級原語,也提供了非常底層的工具,關鍵在於始終選擇能夠解決你的問題的最安全的抽象級別,並最終實現原子代碼。 unsafe 只有當它真正能帶來價值,並且你完全理解其意義時,才值得嘗試。

類型、所有權、借用、函式庫、工具和並發原語等整個生態系統共同構成了一種語言,使你能夠編寫快速、健壯且易於維護的軟體,並最大限度地減少歷史上困擾系統程式設計的多種錯誤。隨著你透過小型專案、Rustlings 等練習以及官方文件進行實踐,這些概念將從看似嚴格的規則轉變為你的得力助手,在 bug 進入生產環境之前發出警告。

Rust 語言實例介紹-0
相關文章:
Rust 完整入門:實用入門指南及範例
艾薩克

Isaac

對字節世界和一般技術充滿熱情的作家。我喜歡透過寫作分享我的知識,這就是我在這個部落格中要做的,向您展示有關小工具、軟體、硬體、技術趨勢等的所有最有趣的事情。我的目標是幫助您以簡單有趣的方式暢遊數位世界。