作為我們的第一個項目,我們來實現一個經典新手編程問題:猜猜看游戲。它是這么工作的:我們的程序將會隨機生成一個1到100之間的隨機數。它接著會提示我們猜一個數。當我們猜了一個數之后,它會告訴我們是大了還是小了。當我們猜對了,它會祝賀我們。聽起來如何?
## 準備
我們準備一個新項目。進入到你的項目目錄。還記得我們曾經創建我們`hello_world`的項目目錄和`Cargo.toml`文件嗎?Cargo有一個命令來為我們做這些。讓我們試試:
~~~
$ cd ~/projects
$ cargo new guessing_game --bin
$ cd guessing_game
~~~
我們將項目名字傳遞給`cargo new`,然后用了`--bin`標記,因為我們要創建一個二進制文件,而不是一個庫文件。
查看生成的`Cargo.toml`文件:
~~~
[package]
name = "guessing_game"
version = "0.0.1"
authors = ["Your Name "]
~~~
Cargo從環境變量中獲取這些信息。如果這不對,趕緊修改它。
最后,Cargo為我們生成了應給“Hello, world!”。查看`src/main.rs`文件:
~~~
fn main() {
println!("Hello, world!")
}
~~~
讓我們編譯Cargo為我們生成的項目:
~~~
$ cargo build
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
~~~
很好,再次打開你的`src/main.rs`文件。我們會將所有代碼寫在這個文件里。稍后我們會講到多文件項目。
在我們繼續之前,讓我們再告訴你一個新的Cargo命令:`run`。`cargo run`跟`cargo build`類似,并且還會運行我們剛生成的可執行文件。試試它:
~~~
$ cargo run
Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
Running `target/guessing_game`
Hello, world!
~~~
很好!`run`命令在我們需要快速重復運行一個項目是非常方便。我們的游戲就是這么一個項目,在我們添加新內容之前我們需要經常快速測試項目。
## 處理一次猜測
讓我們開始吧!我們需要做的第一件事是讓我們的玩家輸入一個猜測。把這些放入你的`src/main.rs`:
~~~
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
~~~
這有好多東西!讓我們一點一點的過一遍。
~~~
use std::io;
~~~
我們需要獲取用戶輸入,并接著打印結果作為輸出。為此,我們需要標準庫的`io`庫。Rust為所有程序只導入了很少一些東西,[‘prelude’](http://doc.rust-lang.org/nightly/std/prelude/)。如果它不在預先導入中,你將不得不直接`use`它。
~~~
fn main() {
~~~
就像你之前見過的,`main()`是你程序的入口點。`fn`語法聲明了一個新函數,`()`表明這里沒有參數,而`{`開始了函數體。因為我們不包含返回類型,它假設是`()`,一個空的[元組](http://doc.rust-lang.org/nightly/book/primitive-types.html#tuples)。
~~~
println!("Guess the number!");
println!("Please input your guess.");
~~~
我們之前學過`println!()`是一個在屏幕上打印[字符串](http://doc.rust-lang.org/nightly/book/strings.html)的[宏](http://doc.rust-lang.org/nightly/book/macros.html)。
~~~
let mut guess = String::new();
~~~
現在我們遇到有意思的東西了!這一小行有很多內容。第一個我們需要注意到的是[let語句](http://doc.rust-lang.org/nightly/book/variable-bindings.html),它用來創建“變量綁定”。它使用這個形式:
~~~
let foo = bar;
~~~
這回創建一個叫做`foo`的新綁定,并綁定它到`bar`這個值上。在很多語言中,這叫做一個“變量",不過Rust的變量綁定暗藏玄機。
例如,它們默認是[不可變的](http://doc.rust-lang.org/nightly/book/mutability.html)。這時為什么我們的例子使用了`mut`:它讓一個綁定可變,而不是不可變。`let`并不從左手邊獲取一個名字,事實上他接受一個[模式(pattern)](http://doc.rust-lang.org/nightly/book/patterns.html)。我們會在后面更多的使用模式。現在它使用起來非常簡單:
~~~
let foo = 5; // immutable.
let mut bar = 5; // mutable
~~~
噢,同時`//`會開始一個注釋,直到這行的末尾。Rust忽略[注釋](http://doc.rust-lang.org/nightly/book/comments.html)中的任何內容。
那么現在我們知道了`let mut guess`會引入一個叫做`guess`的可變綁定,不過我們不得不看看`=`的右側,它綁定的內容:`String::new()`。
`String`是一個字符串類型,由標準庫提供。[String](http://doc.rust-lang.org/nightly/std/string/struct.String.html)是一個可增長的,UTF-8編碼的文本。
`::new()`語法用了`::`因為它是一個特定類型的”關聯函數“。這就是說,它與`String`自身關聯,而不是與一個特定的`String`實例關聯。一些語言管這叫一個”靜態方法“。
這個函數叫做`new()`,因為它創建了一個新的,空的`String`。你會在很多類型上找到`new()`函數,因為它是創建一些類型新值的通常名稱。
讓我們繼續:
~~~
io::stdin().read_line(&mut guess)
.ok()
.expect("Failed to read line");
~~~
這稍微有點多!讓我們一點一點來。第一行有兩部分。這是第一部分:
~~~
io::stdin()
~~~
還記得我們如何在程序的第一行`use``std::io`的嗎?現在我們調用了一個與之相關的函數。如果我們不`use std::io`,那么我們就得寫成`std::io::stdin()`。
這個特殊的函數返回一個句柄到你終端的標準輸入。更具體的,一個[std::io::Stdin](http://doc.rust-lang.org/nightly/std/io/struct.Stdin.html)。
下一部分將用這個句柄去獲取用戶輸入:
~~~
.read_line(&mut guess)
~~~
這里,我們對我們的句柄調用了[read_line()](http://doc.rust-lang.org/nightly/std/io/struct.Stdin.html#method.read_line)方法。[方法](http://doc.rust-lang.org/nightly/book/methods.html)(404)就像關聯函數,不過只在一個類型的特定實例上可用,而不是這個類型本身。我們也向`read_line()`傳遞了一個參數:`&mut guess`。
還記得我們上面怎么綁定`guess`的嗎?我們說它是可變的。然而,`read_line`并不獲取`String`作為一個參數:它獲取一個`&mut String`。Rust有一個叫做[“引用”](http://doc.rust-lang.org/nightly/book/references-and-borrowing.html)的功能,它允許你對一片數據有多個引用,用它可以減少拷貝。引用是一個復雜的功能,因為Rust的一個主要賣點就是它如何安全和便捷的使用引用。然而,目前我們還不需要知道很多細節來完成我們的程序。現在,所有我們需要了解的是像`let`綁定,引用默認是不可變的。因此,我們需要寫成`&mut guess`,而不是`&guess`。
為什么`read_line()`會需要一個字符串的可變引用呢?它的工作是從標準輸入獲取用戶輸入,并把它放入一個字符串。所以它用字符串作為參數,為了可以增加輸入,它必須是可變的。
不過我們還未完全看完這行代碼。雖然它是單獨的一行代碼,它是只是這個單獨邏輯代碼行的開頭部分:
~~~
.ok()
.expect("Failed to read line");
~~~
當你用`.foo()`語法調用一個函數的時候,你可能會引入一個新行符或其它空白。這幫助我們拆分長的行。我們_可以_這么干:
~~~
io::stdin().read_line(&mut guess).ok().expect("failed to read line");
~~~
不過這樣會難以閱讀。所以我們把它分開,3行對應3個方法調用。我們已經談論過了`read_line()`,不過`ok()`和`expect()`呢?好吧,我們已經提到過`read_line()`將用戶輸入放入我們傳遞給它的`&mut String`中。不過他也返回一個值:在這個例子中,一個[io::Result](http://doc.rust-lang.org/nightly/std/io/type.Result.html)。Rust的標準庫中有很多叫做`Result`的類型:一個泛型[Result](http://doc.rust-lang.org/nightly/std/result/enum.Result.html),然后是子庫的特殊版本,例如`io::Result`。
這個`Result`類型的作用是編碼錯誤處理信息。`Result`類型的值,像任何(其它)類型,有定義在其上的方法。在這個例子中,`io::Result`有一個`ok()`方法,它說“我們想假設這個值是一個成功的值。如果不是,就拋出錯誤信息”。為什么要拋出錯誤呢?好吧,對于一個基礎的程序,我們只想打印出一個通用錯誤,因為基本上任何問題意味著我們不能繼續。[ok()方法](http://doc.rust-lang.org/nightly/std/result/enum.Result.html#method.ok)返回一個值,有另一個方法定義在其上:`expect()`。[expect()方法](http://doc.rust-lang.org/nightly/std/option/enum.Option.html#method.expect)獲取調用它的值,而且如果它不是一個成功的值,[panic!](http://doc.rust-lang.org/nightly/book/error-handling.html)并帶有你傳遞給它的信息。這樣的`panic!`會使我們的程序崩潰,顯示(我們傳遞的)信息。
如果我們去掉這兩個函數調用,我們的程序會編譯通過,不過我們會得到一個警告:
~~~
$ cargo build
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
src/main.rs:10:5: 10:39 warning: unused result which must be used,
#[warn(unused_must_use)] on by default
src/main.rs:10 io::stdin().read_line(&mut guess);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~
Rust警告我們我們并未使用`Result`的值。這個警告來自`io::Result`的一個特殊注解。Rust嘗試告訴你你并未處理一個可能的錯誤。阻止錯誤的正確方法是老實編寫錯誤處理。幸運的是,如果我們只是想如果這有一個問題就崩潰的話,我們可以用這兩個小方法。如果我們想從錯誤中恢復什么的,我們得做點別的,不過我們會把它留給接下來的項目。
這是我們第一個例子僅剩的一行:
~~~
println!("You guessed: {}", guess);
}
~~~
這打印出我們保存輸入的字符串。`{}`是一個占位符,所以我們傳遞`guess`作為一個參數。如果我們有多個`{}`,我們應該傳遞多個參數:
~~~
let x = 5;
let y = 10;
println!("x and y: {} and {}", x, y);
~~~
簡單加愉快。
不過怎么說,這只是一個觀光。我們可以用`cargo run`運行我們寫的:
~~~
$ cargo run
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
Running `target/debug/guessing_game`
Guess the number!
Please input your guess.
6
You guessed: 6
~~~
好的!我們的第一部分完成了:我們可以從鍵盤獲取輸入,并把它打印回去。
## 生成一個秘密數字
接下來,我們要生成一個秘密數字。Rust標準庫中還未包含一個隨機數功能。Rust團隊確實,然而,提供了一個[`rand`?crate](https://crates.io/crates/rand)。一個“包裝箱”(crate)是一個Rust代碼的包。我們已經構建了一個”二進制包裝箱“,它是一個可執行文件。`rand`是一個”庫包裝箱“,它包含被認為應該被其它程序使用的代碼。
使用外部包裝箱是Cargo的亮點。在我們使用`rand`編寫代碼之前,我們需要修改我們的`Cargo.toml`。打開它,并在末尾增加這幾行:
~~~
[dependencies]
rand="0.3.0"
~~~
`Cargo.toml`的`[dependencies]`部分就像`[package]`部分:所有之后的東西都是它的一部分,直到下一個部分開始。Cargo使用依賴部分來知曉你用的外部包裝箱的依賴,和你要求的版本。在這個例子中,我們用了`0.3.0`版本。Cargo理解[語義化版本](http://semver.org/lang/zh-CN/),它是一個編寫版本號的標準。如果我們想要使用最新版本我們可以使用`*`或者我們可以使用一個范圍的版本。[Cargo文檔](http://doc.crates.io/crates-io.html)包含更多細節。
現在,在不修改任何我們代碼的情況下,讓我們構建我們的項目:
~~~
$ cargo build
Updating registry `https://github.com/rust-lang/crates.io-index`
Downloading rand v0.3.8
Downloading libc v0.1.6
Compiling libc v0.1.6
Compiling rand v0.3.8
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
~~~
(當然,你可能會看到不同的版本)
很多新的輸出!現在我們有了一個外部依賴,Cargo從記錄中獲取了所有東西的最新版本,它們是來自[Crates.io](https://crates.io/)的一份拷貝。Crates.io是Rust生態系統中人們發表開源Rust項目供它人使用的地方。
在更新了記錄后,Cargo檢查我們的`[dependencies]`并下載任何我們還沒有的東西。在這個例子中,雖然我們只說了我們要依賴`rand`,我們也獲取了一份`libc`的拷貝。這是因為`rand`依賴`libc`工作。在下載了它們之后,它編譯它們,然后接著編譯我們的項目。
如果我們再次運行`cargo build`,我們會得到不同的輸出:
~~~
$ cargo build
~~~
沒錯,木有輸出!Cargo知道我們的項目被構建了,并且所有它的依賴也被構建了,所以沒有理由再做一遍所有這一些。沒有事情做,它簡單的退出了。如果我們再打開`src/main.rs`,做一個無所謂的修改,然后接著再保存,我們就會看到一行:
~~~
$ cargo build
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
~~~
所以,我們告訴Cargo我們需要任何`0.3.x`版本的`rand`,并且因此它獲取在本文被編寫時的最新版,`v0.3.8`。不過你瞧瞧當下一周,`v0.3.9`出來了,帶有一個重要的bug修改嗎?雖然bug修改很重要,不過如果`0.3.9`版本包含破壞我們代碼的回歸呢?
這個問題的回答是現在你會在你項目目錄中找到的`Cargo.lock`。當你第一次構建你的項目的時候,Cargo查明所有符合你的要求的版本,并接著把它們寫到了`Cargo.lock`文件里。當你在未來構建你的項目的時候,Cargo會注意到`Cargo.lock`的存在,并接著使用指定的版本而不是再次去做查明版本的所有工作。這讓你有了一個可重復的自動構建。換句話說,我們會保持在`0.3.8`直到我們顯式的升級,這對任何使用我們共享的代碼的人同樣有效,感謝鎖文件。
當我們_確實_想要使用`v0.3.9`怎么辦?Cargo有另一個命令,`update`,它代表“忽略鎖,搞清楚所有我們指定的最新版本。如果這能工作,將這些版本寫入鎖文件”。不過,默認,Cargo只會尋找大于`0.3.0`小于`0.4.0`的版本。如果你想要移動到`0.4.x`,我們不得不直接更新`Cargo.toml`文件。當我們這么做,下一次我們`cargo build`,Cargo會更新索引并重新計算我們的`rand`要求。
關于[?Cargo](http://doc.crates.io/)和[它的生態系統](http://doc.crates.io/crates-io.html)有很多東西要說,不過眼下,這是我們需要知道的一切。Cargo讓重用庫變得真正的簡單,并且Rustacean們可以編寫更小的由很多子包組裝成的項目。
讓我們確實的_使用_`rand`。這是我們的下一步:
~~~
extern crate rand;
use std::io;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("failed to read line");
println!("You guessed: {}", guess);
}
~~~
我們做的第一件事是修改第一行。現在它是`extern crate rand`。因為我們在我們的`[dependencies]`聲明了`rand`,我們可以用`extern crate`來讓Rust知道我們正在使用它。這也等同于一個`use rand;`,所以我們可以通過`rand::`前綴使用`rand`包裝箱中的一切。
下一步,我們增加了另一行`use`:`use rand::Rng`。我們一會將要使用一個方法,并且它要求`Rng`在作用域中才能工作。這個基本觀點是:方法定義在一些叫做“特性”的東西上面,而為了讓方法能夠工作,需要這個特性位于作用域中。關于更多細節,閱讀[特性](http://doc.rust-lang.org/nightly/book/traits.html)部分。
這里還有兩行我們增加的,在中間:
~~~
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
~~~
我們用`rand::thread_rng()`函數來獲取一個隨機數生成器的拷貝,它位于我們特定的執行[線程](http://doc.rust-lang.org/nightly/book/concurrency.html)的本地。因為我們`use rand::Rng`了,我們有一個`gen_range()`方法可用。這個函數獲取兩個參數,并產生一個位于其間的數字。它包含下限,不過不包含上限,所以我們需要`1`和`101`來生成一個`1`和`100`之間的數。
第二行僅僅打印出了秘密數字。這在我們開發我們的程序時很有用,所以我們可以簡單的測試出來。不過在最終版本中我們會刪除它。在開始就打印出結果就沒什么可玩的了!
嘗試運行我們的新程序幾次:
~~~
$ cargo run
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
Running `target/debug/guessing_game`
Guess the number!
The secret number is: 7
Please input your guess.
4
You guessed: 4
$ cargo run
Running `target/debug/guessing_game`
Guess the number!
The secret number is: 83
Please input your guess.
5
You guessed: 5
~~~
好的!接下來:讓我們比較我們的猜測和秘密數字。
## 比較猜測
現在我們得到了用戶輸入,讓我們比較我們的猜測和隨機值。這是我們的下一步,雖然它還不能正常工作:
~~~
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("failed to read line");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
~~~
這有一些新東西。第一個是另一個`use`。我們帶來了一個叫做`std::cmp::Ordering`類型到作用域中。接著,底部5行代碼使用了它:
~~~
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
~~~
`cmp()`可以在任何能被比較的值上調用,并且它獲取你想要比較的值的引用。它返回我們之前`use`的`Ordering`類型。我們使用一個[match](http://doc.rust-lang.org/nightly/book/match.html)語句來決定具體是哪種`Ordering`。`Ordering`是一個[枚舉](http://doc.rust-lang.org/nightly/book/enums.html),“enumeration”的簡寫,它看起來像這樣:
~~~
enum Foo {
Bar,
Baz,
}
~~~
通過這個定義,任何`Foo`可以是`Foo::Bar`或者`Foo::Baz`。我們用`::`來表明一個特定`enum`變量的命名空間。
[Ordering](http://doc.rust-lang.org/nightly/std/cmp/enum.Ordering.html)枚舉有3個可能的變量:`Less`,`Equal`和`Greater`。`match`語句獲取類型的值,并讓你為每個可能的值創建一個“分支”。因為我們有3種類型的`Ordering`,我們有3個分支:
~~~
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
~~~
如果它是`Less`,我們打印`Too small!`,如果它是`Greater`,`Too big!`,而如果`Equal`,`You win!`。`match`真的灰常有用,并且在Rust中經常使用。
我確實提到過我們還不能正常運行,雖然。讓我們試試:
~~~
$ cargo build
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
src/main.rs:28:21: 28:35 error: mismatched types:
expected `&collections::string::String`,
found `&_`
(expected struct `collections::string::String`,
found integral variable) [E0308]
src/main.rs:28 match guess.cmp(&secret_number) {
^~~~~~~~~~~~~~
error: aborting due to previous error
Could not compile `guessing_game`.
~~~
噢!這是一個大錯誤。它的核心是我們有“不匹配的類型”。Rust有一個強大的靜態類型系統。然而,它也有類型推斷。當我們寫`let guess = String::new()`,Rust能夠推斷出`guess`應該是一個`String`,并因此不需要我們寫出類型。而我們的`secret_number`,這有很多類型可以有從`1`到`100`的值:`i32`,一個32位數,或者`u32`,一個無符號的32位值,或者`i64`,一個64位值。或者其它什么的。目前為止,這并不重要,所以Rust默認為`i32`。然而,這里,Rust并不知道如何比較`guess`和`secret_number`。它們必須是相同的類型。最終,我們想要我們作為輸入讀到的`String`轉換為一個真正的數字類型,來進行比較。我們可以用額外3行來搞定它。這是我們的新程序:
~~~
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("failed to read line");
let guess: u32 = guess.trim().parse()
.ok()
.expect("Please type a number!");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
~~~
新的3行是:
~~~
let guess: u32 = guess.trim().parse()
.ok()
.expect("Please type a number!");
~~~
稍等,我認為我們已經用過了一個`guess`?確實,不過Rust允許我們用新值“遮蓋(shadow)”之前的`guess`。這在這種具體的情況中經常被用到,`guess`開始是一個`String`,不過我們想要把它轉換為一個`u32`。遮蓋(Shadowing)讓我們重用`guess`名字,而不是強迫我們想出兩個獨特的像`guess_str`和`guess`,或者別的什么的。
我們綁定`guess`到一個看起來像我們之前寫的表達式:
~~~
guess.trim().parse()
~~~
后跟一個`ok().expect()`調用。這里,`guess`引用舊的`guess`,那個我們輸入用到的`String`。`String`的`trim()`方法會去掉我們字符串開頭和結尾的任何空格。這很重要,因為我們不得不按“回車”鍵來滿足`read_line()`。這意味著如果我們輸入`5`并按回車,`guess`看起來像這樣:`5\n`。`\n`代表“新行”,回車鍵。`trim()`去掉這些,保留`5`給我們的字符串。[字符串的`parse()`方法](http://doc.rust-lang.org/nightly/std/primitive.str.html#method.parse)將字符串解析為一些類型的數字。因為它可以解析多種數字,我們需要給Rust一些提醒作為我們具體想要的數字的類型。因此,`let guess: u32`。`guess`后面的分號(`:`)告訴Rust我們要標注它的類型。`u32`是一個無符號的,32位整型。Rust有[一系列內建類型](http://doc.rust-lang.org/nightly/book/primitive-types.html#numeric-types),不過我們選擇了`u32`。它是一個小的正數的好的默認選擇。
就像`read_line()`,我們調用`parse()`可能產生一個錯誤。如果我們的字符串包含`A
- 前言
- 1.介紹
- 2.準備
- 2.1.安裝Rust
- 2.2.Hello, world!
- 2.3.Hello, Cargo!
- 3.學習Rust
- 3.1.猜猜看
- 3.2.哲學家就餐問題
- 3.3.其它語言中的Rust
- 4.高效Rust
- 4.1.棧和堆
- 4.2.測試
- 4.3.條件編譯
- 4.4.文檔
- 4.5.迭代器
- 4.6.并發
- 4.7.錯誤處理
- 4.8.外部語言接口
- 4.9.Borrow 和 AsRef
- 4.10.發布途徑
- 5.語法和語義
- 5.1.變量綁定
- 5.2.函數
- 5.3.原生類型
- 5.4.注釋
- 5.5.If語句
- 5.6.for循環
- 5.7.while循環
- 5.8.所有權
- 5.9.引用和借用
- 5.10.生命周期
- 5.11.可變性
- 5.12.結構體
- 5.13.枚舉
- 5.14.匹配
- 5.15.模式
- 5.16.方法語法
- 5.17.Vectors
- 5.18.字符串
- 5.19.泛型
- 5.20.Traits
- 5.21.Drop
- 5.22.if let
- 5.23.trait對象
- 5.24.閉包
- 5.25.通用函數調用語法
- 5.26.包裝箱和模塊
- 5.27.`const`和`static`
- 5.28.屬性
- 5.29.`type`別名
- 5.30.類型轉換
- 5.31.關聯類型
- 5.32.不定長類型
- 5.33.運算符和重載
- 5.34.`Deref`強制多態
- 5.35.宏
- 5.36.裸指針
- 6.Rust開發版
- 6.1.編譯器插件
- 6.2.內聯匯編
- 6.3.不使用標準庫
- 6.4.固有功能
- 6.5.語言項
- 6.6.鏈接參數
- 6.7.基準測試
- 6.8.裝箱語法和模式
- 6.9.切片模式
- 6.10.關聯常量
- 7.詞匯表
- 8.學院派研究
- 勘誤