來源 | 泡芙玩編程(ID:gh_23284b66d001)
前言
Rust 可能有點難學,但我還是建議去學一學,起碼要了解一下它的理念,它是如何做到它所吹的那些特性的,為什么別的語言做不到它做到了,通過學習 Rust 也有可能會改變你之前的一些也許不那么正確的編程方式。哎,也許你會說我平時業務都做不完了,還學這么個破玩意干嘛,工作歸工作,生活歸生活嘛,但是作為一個有點追求的程序員,起碼要學習一門底層系統語言吧,可以開拓自己的視野,跳出自己的一畝三分地也不錯啊,啥,你說你不想學這些?那你寫代碼就是為了混口飯吃,那沒事,了解了解也總行吧,不然以后也不知道為什么別人天天在那說這個東西。
社區狀況
由 Mozilla 開發的 Rust 在過去幾年中已經獲得了主流使用。根據 StackOverflow 的 2023 年開發者調查,Rust 連續第 8 年被評為最受開發者喜愛的語言和開發者最想學習的語言,作為一種注重安全、速度和并發的系統編程語言,Rust 可以和 C++媲美性能,但具有現代語言(如 Python/ target=_blank class=infotextkey>Python 或 JAVA)的人體工程學。目前在許多大公司內部都有使用,比如國內字節、華為、VIVO、國外微軟、谷歌、亞馬遜、DropBox、Cloudflare 等,之前的文章也提到過這些。
廢話不多說了,直接說下 Rust 的相關特性吧。
特性
官方鼓吹的它有以下的一些優點:
-
高性能 API 服務器、分布式系統、高性能機器學習模型、數據科學應用
-
跨平臺桌面應用程序和命令行工具
-
零成本抽象,Rust 提供不犧牲性能的高水平人體工程學(說人話就是優雅)
-
保證內存安全,Rust 的嚴格編譯器在編譯時可以防止空指針異常,數據競爭等等
-
無畏并發,Rust 的類型系統和所有權模式可以保證線程安全
-
健壯的生態系統,Rust 有一個在蓬勃發展的社區
Rust 是高性能的語言,擁有和 C/C++相當的性能。那么它是通過什么來實現這一點的呢?
零成本抽象
Rust 在不犧牲性能的情況下提供抽象。比如,Rust 有可以編譯成 for 循環的迭代器并且沒有性能損失。這意味著可以在不犧牲速度的情況下編寫干凈的代碼,比如下面這段代碼,比你自己手寫一個 for 循環并相加要簡潔得多:
letv = vec![1, 2, 3];
letsum = v.iter.sum; // 這會編譯為 for 循環
口號就是 "高級語句別怕慢,編譯之后都一樣"
移動語義
Rust 具有移動語義,這意味著值的所有權會在作用域之間進行移動,這可以避免開銷比較高的數據復制,比如:
letx = vec![1, 2, 3];
lety = x; // `x` 在這里被移動,并且不能再使用
這里,向量數組被移動到 y 中,而不是復制。注意復制是指數據從一個地方的內存到另一個地方的內存,而移動指的是數據內存沒發生改變,只是指向變了。
沒垃圾回收
Rust 通過所有權和借用規則提供內存安全,而不是垃圾收集器。這避免了運行時進行垃圾回收的性能損失,可以避免在 JS/Java/Go 等語言里"Stop the world"的情況發生。代碼可以在編譯時靜態保證沒有使用后釋放錯誤、懸空指針或數據競爭。
非常小的運行時"Stop the world" 意味著在進行垃圾回收時,程序的執行會被短暫暫停以進行內存回收
Rust 的運行時非常小,不需要運行時類型信息、虛擬機或垃圾收集器,這就能打包出開銷很小的二進制文件,不過有一些場景也是需要用到運行時的,比如動態分發(現在不需要了解,用于在運行時根據傳入類型確定調用方法的一種手段)。
安全可靠
Rust 是主打安全的語言,在編譯時可以防止一些內存上的錯誤,這是通過 Rust 嚴格的借用和所有權規則來實現的。所有權和借用是 Rust 最特殊和最有用的兩個概念。所有權意味著 Rust 中的每個值都有一個擁有它的變量,值的所有者負責釋放與之關聯的資源,當所有者超出范圍時,擁有的值將被刪除。
當對一個值有不可變或可變的引用時,就會發生借用。對于不可變引用,原始所有者仍然擁有該值,但是借方可以讀取它。使用可變引用,借方可以改變值。但是,可變和不可變借用不能共存,并且借用必須在所有者超出作用域范圍之前結束。
上面這里有點繞,但是只要記住:"可變不共享,共享不可變,結束前要還" 這句就行了。下面看 2 個例子:
可變不共享,共享不可變
fnmAIn{
letmutx = 5;
lety = &x; // y 從 x 借用一個不可變引用
letz = &mutx; // y 從 x 借用一個可變借用。報錯,不能在一個作用域內同時出現可變借用和不可變借用
}
結束前要還
fnmain{
letmutx = 5; // x 是可變的,擁有值 5
lety = &mutx; // y 從 x 借用到一個可變引用
*y += 1; // 通過 y 來增加 x 的值,因為 y 是對 x 的可變引用
println!("x is {}", x); // 打印出了 6
} // y 超出了作用域,結束對 x 的引用
無畏并發
Rust 為并發代碼提供了內置支持,這允許 Rust 程序充分利用多核。由于 Rust 的所有權和類型系統,在 Rust 中不可能在編譯時出現數據競爭,這就是所謂的“無畏并發”。
多線程usestd::thread;
fnmain{
thread::spawn(|| {
println!("Hello new thread!");
});
}
這會從新的線程里打印“Hello new thread!”,它是和主線程并行的。
線程通信
通道可以讓消息在線程之間進行傳遞
usestd::thread;
usecrossbeam::channel;
fnmain{
let(tx, rx) = channel::unbounded;
thread::spawn(move|| {
tx.send("Hello from thread!").unwrap;
});
letmsg = rx.recv.unwrap;
println!("Got message: {}", msg); // Got message: Hello from thread!
}
這里我們創建了一個不限制大小的通道并生成一個新的線程,該線程在通道上發送消息,在主線程中接收消息并打印它。
共享狀態
雖然通道對于消息傳遞很有用,但有時線程需要訪問共享的狀態。這可以通過互斥鎖來鎖定對臨界區的訪問,這個程序生成 10 個線程,每個線程增加一個共享計數器。通過使用互斥鎖,可以確保一次只有一個線程可以訪問計數器增加的臨界區。
usestd::{
sync::{Arc, Mutex},
thread,
};
fnmain{
letcounter = Arc::new(Mutex::new(0));
letcounters = vec![counter.clone; 10];
forcounter in&counters {
letcounter = counter.clone;
thread::spawn(move|| {
letmutnum = counter.lock.unwrap;
*num += 1;
});
}
forcounter in&counters {
letnum = counter.lock.unwrap;
println!("{}", *num);
}
}
在 C/C++ 中實現并發可要小心翼翼,一不小心就出內存問題了。
完善的工具鏈
Rust 有一套很好用的工具生態,比 C/C++ 的都要完善,比如:
-
Cargo:類似于 NPM 的 Rust 包管理器和構建工具
-
Rustup:有點類似于 nvm,用來管理 Rust 版本(穩定版,測試版,夜間版)。
-
Rustfmt:類似于 Prettier,代碼風格格式化
-
Clippy:類似于 ESLint,Rust 的靜態代碼分析工具
Rust 是一種現代編程語言,它提供了速度、安全性和并發性。Rust 通過零成本抽象、移動語義、保證內存安全以及最小的運行時實現了與 C 和 C++相當的性能。Rust 編譯器嚴格執行關于所有權、借用和生命周期的規則,可以防止各種錯誤如懸空指針、使用后釋放錯誤和數據競爭等問題。此外,Rust 還對并發性和并行性有很好的支持,編譯器會在編譯時防止數據競爭,這可以讓你寫并發代碼時更加從心。
我是一個搞前端的,也在學 Rust。不管怎樣,通過學 Rust 還是能夠獲得比前端這個領域里更多的東西,學一學總沒錯,實在不行你來了解一下也行嘛。
-
Rust中的New Type解答了我十多年前的一個疑問
-
Rust 與 C++:一場現代編程語言的較量
-
2024年,Rust和Go學哪個更好?
-
vivo發布自研操作系統藍河 (BlueOS),系統框架采用Rust編寫
-
這些年來 Rust 在前端都干了些啥?