README.md
commit 3a6dbb30a21be8d237055479af613e30415b0c56
歡迎閱讀!這本書將教會你使用Rust編程語言。Rust 是一個注重安全與速度的現(xiàn)代系統(tǒng)編程語言,通過在沒有垃圾回收的情況下保證內(nèi)存安全來實現(xiàn)它的目標(biāo),這使它成為一個在能夠許多其它語言并不適合的用例中大展身手的語言:嵌入到其它語言中,在特定的時間和空間要求下編程,和編寫例如設(shè)備驅(qū)動和操作系統(tǒng)這樣的底層代碼。它通過一系列的不產(chǎn)生運行時開銷的編譯時安全檢查來提升目前語言所關(guān)注的領(lǐng)域,同時消除一切數(shù)據(jù)競爭。Rust同時也意在實現(xiàn)“零開銷抽象”,即便在這些抽象看起來比較像一個高級語言的特性。即便如此,Rust也允許你像一個底層語言那樣進行精確的控制。
《Rust編程語言》被分為數(shù)個部分。這個介紹是第一部分。之后是:
在閱讀了介紹這部分之后,你可以根據(jù)喜好深入到“學(xué)習(xí)Rust”或“語法和語義”部分:如果你想通過項目深入了解,可以先選擇“學(xué)習(xí) Rust”;如果你想從頭開始,并且學(xué)習(xí)一個完整的內(nèi)容再學(xué)習(xí)另一個,你可以從“語法和語義”開始。豐富的交叉連接將這些部分聯(lián)系到一起。
生成這本書(英文版)的源文件可以在 GitHub 上找到。
以下內(nèi)容在最新版中并未出現(xiàn),暫時保留
Rust 是你會感興趣的語言嗎?讓我們檢查一些小的代碼例子來展示它的部分威力。
使 Rust 顯得獨一無二的主要概念是“所有權(quán)”??紤]這個小例子:
fn main() {
let mut x = vec!["Hello", "world"];
}
這個程序創(chuàng)建了一個叫做x
的[變量綁定](./5.1.Variable Bindings 變量綁定.md)。這個綁定的值是一個Vec<T>
,一個 vector,我們通過一個定義在標(biāo)準(zhǔn)庫中的[宏](./5.34.Macros 宏.md)來創(chuàng)建它。這個宏叫做vec
,并且我們通過一個!
調(diào)用宏。這遵循了 Rust 的一般原則:讓一切明了。宏可以做比函數(shù)調(diào)用復(fù)雜的多的多的工作,并且它們在視覺上也是有區(qū)別的。!
也方便了解析,更容易編寫工具,這也是很重要的。
我們使用了mut
來使x
可變:在 Rust 中綁定是默認(rèn)是不可變的。在下面的例子中這個 vector 是可變的。
另外值得注意的是這里我們并不需要一個類型注釋:因為 Rust 是靜態(tài)類型的,我們并不需要顯式的標(biāo)明類型。Rust 擁有類型推斷來平衡靜態(tài)類型的能力和類型注釋的冗余。
Rust 與堆分配相比傾向于棧分配:x
被直接儲存在棧上。然而,Vec<T>
類型在堆上為 vector 的元素分配了空間。如果你并不熟悉這里的區(qū)別,目前你可以忽略它,或者看看[“棧和堆”](./4.1.The Stack and the Heap 棧和堆.md)。作為一個系統(tǒng)編程語言,Rust 給予你控制內(nèi)存分配的能力,不過當(dāng)我們上手后,這并不是什么大問題。
之前,我們提到“所有權(quán)”是 Rust 中的一個關(guān)鍵概念。在 Rust 用語中,x
被認(rèn)為“擁有”這個 vector。這意味著當(dāng)x
離開作用域,vector 的內(nèi)存將被銷毀。這由 Rust 編譯器決定,而不是通過類似垃圾回收器這樣的機制。換句話說,在 Rust 中,你并不需要自己調(diào)用像malloc
和free
這樣的函數(shù):編譯器靜態(tài)決定何時你需要分配和銷毀內(nèi)存,并自動調(diào)用這些函數(shù)。人非圣賢孰能無過,不過編譯器永遠也不會忘記。
讓我們?yōu)槔釉偌右恍校?/p>
fn main() {
let mut x = vec!["Hello", "world"];
let y = &x[0];
}
我們引入了另一個綁定,y
。在這個例子中,y
是對 vector 第一個元素的“引用”。Rust 的引用類似于其它語言中的指針,不過帶有額外的編譯時安全檢查。引用用[“借用”](./5.8.References and Borrowing 引用和借用.md)它指向的內(nèi)容,而不是擁有它,來與所有權(quán)系統(tǒng)交互。這里的區(qū)別是,當(dāng)一個引用離開作用域,它不會釋放之下的內(nèi)存。如果它這么做了,我們會釋放兩次,這是很糟的!。
讓我們增加第三行。這看起來并不會引起錯誤,不過實際上會造成一個編譯錯誤:
fn main() {
let mut x = vec!["Hello", "world"];
let y = &x[0];
x.push("foo");
}
push
是 vector 的一個方法,它在 vector 的末尾附加另一個元素。當(dāng)我們嘗試編譯這個程序時,我們得到一個錯誤:
error: cannot borrow `x` as mutable because it is also borrowed as immutable
x.push(4);
^
note: previous borrow of `x` occurs here; the immutable borrow prevents
subsequent moves or mutable borrows of `x` until the borrow ends
let y = &x[0];
^
note: previous borrow ends here
fn main() {
}
^
噢!Rust 編譯器有時給出灰常詳細(xì)的錯誤,而這就是其中之一。正如錯誤所解釋的,當(dāng)我們讓綁定可變,我們?nèi)圆荒苷{(diào)用push
。這是因為我們已經(jīng)有了一個 vector 元素的引用,y
。當(dāng)有其它引用存在時改變值是危險的,因為我們可能使這個引用無效。在這個特定的例子中,當(dāng)我們創(chuàng)建了 vector,我們可能只分配了 3 個元素的空間。增加一個元素意味著將分配一個新的能放下所有 4 個元素的空間,拷貝舊的值,并更新內(nèi)部的指針指向這個內(nèi)存。所有這些都木有問題。問題是y
并沒有被更新,很糟糕地,y
成了一個“懸垂指針”(dangling pointer)。因此,在這個例子中任何對y
的使用都會引起錯誤,而編譯器會為我們捕獲了這個錯誤。
那么我們應(yīng)該如何解決這個問題呢?這里我們可以采取兩個方法。第一個方法是使用拷貝而不使用引用:
fn main() {
let mut x = vec!["Hello", "world"];
let y = x[0].clone();
x.push("foo");
}
Rust 默認(rèn)擁有[移動語義](./5.7.Ownership 所有權(quán).md#移動語義),所以如果我們想要拷貝一些數(shù)據(jù),我們調(diào)用clone()
方法。在這個例子中,y
不再是一個儲存在x
中 vector 的一個引用,而是它第一個元素的拷貝,"hello"
?,F(xiàn)在我們并不擁有一個引用,所以push()
就能正常工作。
如果我們真心需要一個引用,我們需要另一種方法:確保在我們嘗試修改之前,讓引用離開作用域。如下:
fn main() {
let mut x = vec!["Hello", "world"];
{
let y = &x[0];
}
x.push("foo");
}
我們用一對大括號創(chuàng)建了一個內(nèi)部作用域,y
會在我們調(diào)用push()
之前離開作用域,所以我們不會碰到問題。
所有權(quán)的概念并不僅僅善于防止懸垂指針,也解決了一整個系列的相關(guān)問題,比如迭代器無效,并發(fā)和其它問題。