歡迎學(xué)習(xí)本教程!本教程將教你如何使用 Rust 編程語(yǔ)言。Rust 是一門強(qiáng)調(diào)安全、性能和并發(fā)性的系統(tǒng)編程語(yǔ)言。它為了達(dá)到這幾個(gè)目的,甚至沒(méi)有一個(gè)垃圾收集器。這也使 Rust 能夠應(yīng)用到其他語(yǔ)言做不到的地方:嵌入到其他語(yǔ)言,有指定空間和時(shí)間需求的程序,寫(xiě)底層代碼(如設(shè)備驅(qū)動(dòng)程序和操作系統(tǒng))。針對(duì)當(dāng)前的其他編程語(yǔ)言,Rust 做到了沒(méi)有運(yùn)行時(shí)(Runtime),沒(méi)有數(shù)據(jù)競(jìng)爭(zhēng)。 Rust 也致力于實(shí)現(xiàn)“零成本抽象”,盡管這些抽象給人的感覺(jué)像一個(gè)高級(jí)的語(yǔ)言。即使是這樣,Rust 仍然可以做到像一個(gè)低級(jí)的語(yǔ)言那樣的精確控制。
“Rust 編程語(yǔ)言”分為七個(gè)部分。本文的簡(jiǎn)介是第一個(gè)。在這之后:
閱讀本文之后,你會(huì)想了解“學(xué)習(xí) Rust”或“語(yǔ)法和語(yǔ)義”,根據(jù)你的喜好:如果你想嘗試一個(gè)項(xiàng)目,可以學(xué)習(xí) “學(xué)習(xí) Rust”章節(jié);或者如果你喜歡從小的部分開(kāi)始,徹底的學(xué)習(xí)了一個(gè)概念之后才移動(dòng)到下一個(gè)概念,那么你可以學(xué)習(xí)“語(yǔ)法和語(yǔ)義”章節(jié)。豐富的交叉聯(lián)合使這些部分連接到一起。
本教程的源文件可以在 Github 上找到: github.com/rust-lang/rust/tree/master/src/doc/trpl
Rust 是你可能會(huì)感興趣的一門語(yǔ)言么?讓我們先來(lái)看看一些能展示其一些優(yōu)勢(shì)的小代碼示例。
讓 Rust 變得獨(dú)特唯一的一個(gè)主要的概念是名稱為“所有權(quán)”的概念。思考下面這個(gè)小例子:
fn main() {
let mut x = vec!["Hello", "world"];
}
這個(gè)程序有一個(gè)變量綁定名稱為 x。此綁定的值是一個(gè) Vec<T>
,是我們通過(guò)在標(biāo)準(zhǔn)庫(kù)中定義的宏創(chuàng)建的一個(gè)'向量'。這個(gè)宏被稱為 Vec,我們利用!調(diào)用宏用。這遵循 Rust 的一般原則:做事情要明確。宏可以有效的做一些比函數(shù)調(diào)用更復(fù)雜的東西,所以他們外觀上來(lái)看是不一樣的。這個(gè)符號(hào)!也有助于解析,使代碼更容易編寫(xiě),這也很重要。
我們使用 mut 使 x 可變:默認(rèn)情況下,Rust 中的綁定是不可變的。我們將在后面的例子中改變此向量。
另外值得一提的是,在這里我們不需要標(biāo)注類型:Rust 是靜態(tài)類型,我們并不需要再明確標(biāo)注類型。Rust 有類型推斷,用以平衡強(qiáng)大的靜態(tài)類型和冗長(zhǎng)標(biāo)注類型。
Rust 更傾向于堆棧分配而不是堆分配:x 被直接放置在堆棧中。然而,Vec<T>
類型是在堆上分配的向量元素的空間。如果你不熟悉它們之間的區(qū)別,那么你現(xiàn)在可以先忽略它,或者你可以查看章節(jié)“堆棧和堆”,來(lái)了解詳細(xì)了解。作為一個(gè)系統(tǒng)編程語(yǔ)言,Rust 賦予了你如何分配內(nèi)存空間的能力,但是在我們開(kāi)始的階段,這是并不是一個(gè)大問(wèn)題。
此前,我們提到的“所有權(quán)”是鐵銹的關(guān)鍵新概念。生銹的說(shuō)法,X 被說(shuō)成“自己”的載體。這意味著,當(dāng) x 超出范圍,載體的存儲(chǔ)器將被解除分配。這是由防銹編譯確定性完成,而不是通過(guò)一個(gè)機(jī)制,諸如垃圾收集器。換句話說(shuō),在防銹,你不叫喜歡的 malloc 函數(shù)和釋放自己:編譯靜態(tài)判斷,當(dāng)你需要分配或釋放內(nèi)存,并插入這些調(diào)用本身。犯錯(cuò)是做人,但編譯器永遠(yuǎn)不會(huì)忘記。
前面我們所提到的,“所有權(quán)”是 Rust 中的一個(gè)非常重要的新概念。按照 Rust 的說(shuō)法,x 被稱為向量“所有”。這意味著當(dāng) x 超出范圍,向量的內(nèi)存將被銷毀。這樣做是由 Rust 的編譯器所決定的,而不是通過(guò)一種機(jī)制(如垃圾收集器)所決定的。換句話說(shuō),在 Rust 中,你不需要自己調(diào)用函數(shù),如 malloc
和 free yourself:
當(dāng)你需要分配或釋放的內(nèi)存時(shí),編譯器會(huì)自行靜態(tài)的決定并插入這些調(diào)用函數(shù)。犯錯(cuò)是人之常情,但編譯器永遠(yuǎn)不會(huì)忘記插入這些調(diào)用的函數(shù)。
讓我們?cè)谖覀兩厦娴睦又刑砑恿硗獾囊恍写a:
fn main() {
let mut x = vec!["Hello", "world"];
let y = &x[0];
}
我們?cè)诖私榻B了另外的一種綁定 ,y
。在這種情況下,y
是向量第一個(gè)元素的一個(gè)“引用”。Rust 的引用與其他語(yǔ)言中的指針類似,不同的是有額外的編譯時(shí)安全檢查。特別指出的是,引用與所有權(quán)系統(tǒng)通過(guò)“借用”來(lái)相互作用,而不是通過(guò)擁有它。不同的是,當(dāng)引用超出范圍時(shí),它不會(huì)釋放底層的內(nèi)存。如果是那樣,我們將會(huì)釋放兩次內(nèi)存,這是顯然是不正確的!
讓我們來(lái)添加第三行代碼。表面上來(lái)是沒(méi)錯(cuò)的,但是它會(huì)導(dǎo)致一個(gè)編譯錯(cuò)誤:
fn main() {
let mut x = vec!["Hello", "world"];
let y = &x[0];
x.push("foo");
}
push 是將一個(gè)元素附加到向量組末端的方法。當(dāng)我們?cè)噲D編譯這個(gè)程序時(shí),我們將得到一個(gè)錯(cuò)誤:
error: cannot borrow `x` as mutable because it is also borrowed as immutable
x.push("foo");
^
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ì)的錯(cuò)誤,這是其中的一次。如錯(cuò)誤解釋中所說(shuō),雖然我們的綁定是可變的,但我們?nèi)圆荒苷{(diào)用 push 方法。這是因?yàn)槲覀円呀?jīng)有了一個(gè)矢量元素的引用 ,y
。當(dāng)另外的一個(gè)引用存在時(shí),改變某些變量是危險(xiǎn)的行為,因?yàn)槲覀兛赡軐?dǎo)致引用的無(wú)效。在這個(gè)特定的例子中,當(dāng)創(chuàng)建向量時(shí),我們可能只分配了三個(gè)元素的空間。添加第四個(gè)元素意味著所有這些元素將會(huì)被分配一塊新的塊內(nèi)存,同時(shí)復(fù)制舊值到新內(nèi)存,更新內(nèi)部指針到新內(nèi)存。這些工作都沒(méi)有什么問(wèn)題。問(wèn)題是, y 不會(huì)更新,所以我們會(huì)有一個(gè)“懸空指針”。那就不對(duì)了。在這種情況下,任何的使用引用 y,將會(huì)導(dǎo)致錯(cuò)誤,所以編譯器已經(jīng)幫我們捕捉到了這個(gè)錯(cuò)誤。
那么,我們?nèi)绾谓鉀Q這個(gè)問(wèn)題呢?有兩種我們可以采用的方法。第一種方法,我們利用拷貝而不是一個(gè)引用:
fn main() {
let mut x = vec!["Hello", "world"];
let y = x[0].clone();
x.push("foo");
}
在默認(rèn)情況下,Rust 有移動(dòng)語(yǔ)義,所有如果我們想要復(fù)制一些數(shù)據(jù),我們可以調(diào)用 clone() 方法。在這個(gè)例子中 ,y
不再是存儲(chǔ)在 x 中向量的一個(gè)引用,而是它的第一個(gè)元素“ Hello ”的一個(gè)副本。現(xiàn)在沒(méi)有引用,我們的 push() 方法可以很好地運(yùn)行。
如果我們真的想用一個(gè)引用,我們需要另外一種選擇:確保在我們嘗試做改變之前,我們的引用跳出其作用域。寫(xiě)法看起來(lái)像這樣:
fn main() {
let mut x = vec!["Hello", "world"];
{
let y = &x[0];
}
x.push("foo");
}
我們用一組額外的大括號(hào)創(chuàng)建了一個(gè)內(nèi)部作用域。在我們調(diào)用 push()
之前,y 已經(jīng)跳出其作用域。所以這樣是可行的。
所有權(quán)的概念不僅有利于防止懸空指針,而且有利于解決與其相關(guān)的所有問(wèn)題,如迭代器失效,并發(fā)性等等。