一個小眾現象是如何連續五年成為StackOverflow最受歡迎的語言的
有時候舊東西比你想像的更有價值。
來自過去的技術來拯救未來。這就是《Rust》的創造者格雷頓·霍爾(Graydon Hoare)所說的他想要實現的目標。
這是Rust的關鍵特徵之一:使用學術界眾所周知但很少在當代程式語言中實現的技術。老舊、可靠、有時被遺忘的技術。但最重要的是,它運行得非常好。
這些技術主要用於一件事:安全。
聽起來無聊嗎?如果你問社區裡的人,答案是否定的。根據今年的StackOverflow開發人員調查,高達86.1%的Rust開發人員最喜歡這門語言,使它成為自2016年以來最受歡迎的語言。
你可能會認為軟體開發人員是這個星球上最具創新精神的人。然而,Rust與「快速行動,打破常規」的咒語正好相反。儘管如此,Rust開發人員幾乎可以保證學習他們以前從未聽說過的概念。
從一些開發人員使用代數數據類型進行系統編程的新奇性,到Rust自己的內存安全方法:每個開發人員都可以找到一些新的、非常有用的東西來學習。還有更多的理由愛上Rust。
無需垃圾收集,更安全的內存
每一種程式語言都面臨著一個挑戰,即以一種安全有效的方式管理計算機的內存。例如,Python有一個垃圾收集器,它會在程序運行時不斷查找不再使用的內存並清理它。
在其他語言中,如C和c++,程式設計師必須顯式地分配和釋放內存。由於所有與內存相關的問題都在程序運行之前被清除,因此這種方法對於優化性能更好。
另一方面,內存是開發人員需要一直考慮的另一件事。這就是為什麼用C編寫程序比用Python要花更長的時間的原因之一,即使它在一天結束時做的事情是一樣的。
Rust採用另一種方式:在編譯時通過所有權系統分配內存。這是一種巧妙的技巧,可以確保清理未使用的數據,而不必強迫程式設計師一直考慮分配和釋放內存。
Rust使用舊的技術進行有效的內存管理
基本上,所有權是三個規則的集合:
Rust中的每個值都有一個名為owner的變量。
一次只能有一個所有者。
當所有者超出作用域時,值將被刪除,從而釋放內存。
一個簡單的例子是在Rust中賦值一個vector:
fn main() {
let a = vec![1, 2, 3];
let b = a;
println!("a: {:?}", b);
}
在第二行中,創建所有者為a的向量[1,2,3]。之後,b成為向量的所有者。因為在print語句中調用了正確的所有者,這個程序在執行時編譯並返回預期的結果:
a: [1, 2, 3]
另一方面,你可以嘗試調用vector和它之前的所有者a,就像這樣:
fn main() {
let a = vec![1, 2, 3];
let b = a;
println!("a: {:?}", b, a);
}
在這種情況下,編譯器拋出一個錯誤,因為在第三行中已經刪除了a。這個主題有更多的深度,但這是基本的思想。
相比之下,Python會在第二種情況下運行。它的垃圾收集器只會在最後一次調用它之後才丟棄,這對開發人員來說很好,但在內存空間方面就不太好了。
在C中,事情會稍微複雜一些:您必須為a分配內存空間,然後將其指向vector,然後為b分配更多的內存空間,將b指向a,最後在您完成時釋放a和b佔用的空間。
從這個意義上說,Rust到內存的方法是開發速度和性能之間的妥協。雖然它不像Python那麼容易編寫,但是一旦您理解了所有權的概念,它就不會像C那樣笨拙。
另一方面,效率是相當驚人的:例如,開發團隊Tilde在Rust中重寫了一些JavaHTTP片段後,設法減少了90%的內存使用。
誰說Rust不能吸引人?
靜態類型而不太難看
這幾乎是動態類型和靜態類型愛好者之間的一場宗教戰爭。雖然用具有動態類型的語言編寫軟體要容易得多,但代碼可能很快變得難以維護。這就是為什麼Python代碼比C代碼更難維護的原因之一。
另一方面,必須聲明每個變量的類型C-style會非常煩人。如果你曾經嘗試在返回C浮點類型的函數中使用double,你就會明白我的意思。
Rust走了一條中間的路:它是一個靜態類型系統,但它只需要程式設計師指定頂級類型,如函數參數和常量。在函數體內部,允許使用python風格的類型推斷。
Rust的一個特別有用的特性是,它也沒有任何類型。這允許您在編譯時處理異常,從而保證程序在最終用戶中平穩運行。考慮這個例子,我們可以得到一個人的全名,不管他是否有中間名:
fn get_full_name(fname: &str, mname: Option, lname: &str) -> String {
match mname {
Some(n) => format!("{} {} {}", fname, n, lname),
None => format!("{} {}", fname, lname),
}
}
fn main() {
println!("{}", get_full_name("Ronald", None, "McDonald"));
println!("{}", get_full_name("Dwight", Some("P."), "Eisenhower"));
}
雖然在其他語言中也有None的版本,但它以一種簡潔的方式展示了Rust的雄心:在保持代碼儘可能持久和可維護性的同時,不讓編寫變得過於困難。
一種極好的系統編程方法
雖然Python是一種通用程式語言,但Rust像C一樣,是用於系統編程的。雖然Rust不是為最終用戶製作應用程式的理想語言,但它非常適合構建為其他軟體提供服務的軟體。
因此,效率是Rust的核心。最好的例子是零成本抽象,它解釋代碼的同時將內存使用保持在最低限度。正如c++的發明者Bjarne Stroustrup所說:「不用的東西,不用付錢。」而且:你所使用的,你不能再更好的手動代碼。」
例如,考慮在Python中把不超過1000的所有整數相加:
sum(range(1000))
這在每次代碼運行時都要進行1000次迭代和添加—您可以想像這會使代碼變慢多少。相比之下,在Rust方面考慮同樣的事情:
(0..1000).sum()
這個編譯成常數499500。實際上,內存使用量已經減少了1 / 1000。
雖然這些抽象在C語言中也存在,但是Rust大量地使用了它們——事實上,一個目標就是在語言中添加儘可能多的零成本抽象。在這個意義上,Rust有點像下一個級別的C。
C已經存在了40多年,Rust的目標也是如此。Rust非常強調向後兼容,所以今天您仍然可以在Rust 1.0中運行代碼。同樣地,如果您今天編寫Rust代碼,您應該仍然能夠在二十年後運行它。Rust不會生鏽!
一個小而不可思議的社區
它強調安全性和可持續性,所有漂亮的細節都說明了這一點,也難怪Dropbox在《Rust》中重寫了許多核心結構。Rust的第一個大讚助商Mozilla在其中編寫了Firefox的重要部分。微軟認為C和c++對於關鍵任務軟體不再安全,並在Rust問題上投入了越來越多的資金。
不僅是大公司,對Rust的熱愛也影響到個人程式設計師。儘管到目前為止StackOverflow的調查對象中只有5%的人使用Rust,但這些開發人員對該語言非常有熱情。
這是有原因的。不僅語言規範和編譯器是經過深思熟慮的。有rustup安裝和管理工具鏈。還有Cargo,這是一個命令行工具,它隨Rust安裝而來,幫助管理依賴關係、運行測試和生成文檔。
板條箱。用戶可以共享和發現庫和文檔。在rs中記錄它們。有來自Clippy的編譯器lints和來自rustfmt的自動格式化。
除此之外,還有官方和非官方的聊天、看板、用戶論壇、StackOverflow問題和世界各地的會議。在一個把友善看得高於一切的社區裡,還有什麼更好的要求嗎?
缺點:在你會走路之前,你需要先跑
關於Rust的一個令人沮喪的事情是高昂的啟動成本。雖然在大多數語言中,你需要一到兩天的時間來學習,但這更像是一到兩周的「Rust」。
這是因為有許多其他語言沒有使用的新概念,而且在編譯時通常會有很多錯誤。您需要在第一天就處理所有異常,不能像在Python中那樣編寫一個臨時代碼來運行並在以後添加異常。
此外,由於Rust仍然是相當新的,並不是所有您可能需要的庫都在那裡。除了官方文檔和StackOverflow上的各種問題,也沒有那麼多的教程。
好消息是,一旦你掌握了這些概念並編譯了你的程序,它就會像魔法一樣運行。另外,考慮到向後兼容性,它應該在20年內仍然可以工作。
考慮到您的代碼的可持續性,以及Rust是由許多大公司支持的事實,一到兩周的預先學習可能是值得的,儘管有缺點。
This type of Rust won’t make your ship sink.
底線是:無所畏懼地進行攻擊
Rust不僅僅是安全。但難以否認的是,它的許多核心概念旨在消除內存洩漏和其他安全問題。在一個軟體就是一切的時代,安全是必須的。
可能每一種即將到來的語言都有其發展空間:Go越來越多地填充Python和Java的空間,Julia在數據科學領域追逐Python,而Rust則在Python和c++的領域中成長。Rust的特別之處在於它令人難以置信的社區,它的創新特性,以及它被設計用於未來幾十年的事實。
還有很多工作要做,其中只有一小部分可以而且將會生鏽。今天的新語言有很大的機會堅持一段時間,即使其他語言也會在未來幾年出現。但如果我必須把名片放在一種語言上,Rust將是一個安全的賭注。