Skip to content

Commit 8c2a047

Browse files
authored
Merge pull request #215 from tibitTW/consistent-translation-of-valid
fix: make translation of "valid" be consistent
2 parents 969ed23 + ccf06a0 commit 8c2a047

File tree

3 files changed

+14
-15
lines changed

3 files changed

+14
-15
lines changed

src/ch19-01-unsafe-rust.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
到目前為止,我們討論的所有程式碼都在編譯期強制加上 Rust 記憶體安全保證。然而,Rust 內部其實隱藏了第二種語言,並不強制加上這些記憶體安全保證:這語言叫做**不安全(unsafe)的 Rust**,可和常規 Rust 一樣正常執行,同時賦予我們極強的能力。
44

5-
不安全的 Rust 之所以存在,是由於靜態分析本質上過於保守。當編譯器嘗試確認程式碼是否遵守這些安全保證時,比起接受一些非法的程式,更寧願拒絕部分合法程式。儘管有些程式碼**看起來**正確,但 Rust 無法獲取足夠資訊保證的話,它就是會擋下來。在這些案例中,你可以寫不安全程式碼並告訴編譯器:「相信我,我知道我在幹麻。」從反面來看這也有缺點,你必須自行承擔風險:若誤用不安全程式碼,可能會造成記憶體不安全,例如發生對空指標(null pointer)解參考。
5+
不安全的 Rust 之所以存在,是由於靜態分析本質上過於保守。當編譯器嘗試確認程式碼是否遵守這些安全保證時,比起接受一些非法的程式,更寧願拒絕部分有效的程式。儘管有些程式碼**看起來**正確,但 Rust 無法獲取足夠資訊保證的話,它就是會擋下來。在這些案例中,你可以寫不安全程式碼並告訴編譯器:「相信我,我知道我在幹麻。」從反面來看這也有缺點,你必須自行承擔風險:若誤用不安全程式碼,可能會造成記憶體不安全,例如發生對空指標(null pointer)解參考。
66

77
Rust 擁有另一個不安全的自我的另一理由是電腦硬體本質上就不安全。如果 Rust 不允許這些不安全操作,就無法完成特定任務。Rust 必須允許你做這些底層系統程式設計,例如直接與作業系統互動,甚至撰寫自己的作業系統。系統程式設計是這個語言的目標之一,一起探索我們可以用不安全的 Rust 做什麼和如何使用吧。
88

@@ -18,7 +18,7 @@ Rust 擁有另一個不安全的自我的另一理由是電腦硬體本質上就
1818

1919
需要謹記在心的是,`unsafe` 並不會關閉借用檢查器(borrow checker)或是停用其他 Rust 的安全檢查:在不安全程式碼中操作一個參考仍然會經過檢查。`unsafe` 關鍵字只提供上述不經由編譯器檢查記憶體安全的五項功能,在不安全區塊內你依然保有一定程度的安全性。
2020

21-
此外,`unsafe` 並不意味在此區塊內的程式碼一定有風險或有記憶體安全問題:其目的是作為一個程式設計師,你必須確保在 `unsafe` 區塊內的程式碼透過合法途徑存取記憶體
21+
此外,`unsafe` 並不意味在此區塊內的程式碼一定有風險或有記憶體安全問題:其目的是作為一個程式設計師,你必須確保在 `unsafe` 區塊內的程式碼透過有效途徑存取記憶體
2222

2323
錯誤因人類不可靠而發生。不過,將五種不安全操作標記在 `unsafe` 區塊內,讓你得知任何記憶體安全相關的錯誤一定在某個 `unsafe` 內。請將 `unsafe` 區塊保持夠小,當你在調查一個記憶體錯誤時,會慶幸當初有這麼做。
2424

@@ -28,12 +28,12 @@ Rust 擁有另一個不安全的自我的另一理由是電腦硬體本質上就
2828

2929
### 對裸指標解參考
3030

31-
在第四章[「迷途參考」][迷途參考]一節,我們提及編譯器確保參考一定是合法的。不安全的 Rust 有兩種新型別叫**裸指標**,和參考非常相似。和參考一樣,裸指標能是不可變或可變,分別寫做 `*const T``*mut T`。星號不是解參考運算子,它就是型別名稱的一部分。在裸指標的脈絡下,**不可變**代表指標不能在被解參考之後直接賦值。
31+
在第四章[「迷途參考」][迷途參考]一節,我們提及編譯器確保參考一定是有效的。不安全的 Rust 有兩種新型別叫**裸指標**,和參考非常相似。和參考一樣,裸指標能是不可變或可變,分別寫做 `*const T``*mut T`。星號不是解參考運算子,它就是型別名稱的一部分。在裸指標的脈絡下,**不可變**代表指標不能在被解參考之後直接賦值。
3232

3333
和參考與智慧指標(smart pointer)不同,裸指標是:
3434

3535
* 允許忽略借用規則,同時可存在指向相同位置的可變和不可變的指標,或是多個可變指標
36-
* 不能保證一定指向合法記憶體
36+
* 不能保證一定指向有效記憶體
3737
* 可以為空(null)
3838
* 並無實作任何自動清理機制
3939

@@ -49,9 +49,9 @@ Rust 擁有另一個不安全的自我的另一理由是電腦硬體本質上就
4949

5050
注意,這段程式碼並無使用 `unsafe` 關鍵字。我們可以在安全程式碼中建立裸指標,我們只是不能在不安全區塊外對其解參考,你很快就會看到。
5151

52-
我們透過 `as` 將不可變與可變參考轉型成個別對應的裸指標。由於這些裸指標是從保證合法的參考而來,就能得知這些裸指標同樣合法,但我們無法推導所有裸指標都合法
52+
我們透過 `as` 將不可變與可變參考轉型成個別對應的裸指標。由於這些裸指標是從保證有效的參考而來,就能得知這些裸指標同樣有效,但我們無法推導所有裸指標都有效
5353

54-
為了展示上述情形,接下來,我們將建立無法確認合法性的裸指標,範例 19-2 展示了如何從任意記憶體的位置建立裸指標。嘗試使用任意的記憶體行為並未定義,該位址上可能有也可能沒資料,且編譯器可能會最佳化該程式,所以該處可能不會存取記憶體,或是程式因區段錯誤導致崩潰。一般情況下,雖然這種程式碼能寫得出來,但不會有任何好理由寫出它。
54+
為了展示上述情形,接下來,我們將建立無法確認有效性的裸指標,範例 19-2 展示了如何從任意記憶體的位置建立裸指標。嘗試使用任意的記憶體行為並未定義,該位址上可能有也可能沒資料,且編譯器可能會最佳化該程式,所以該處可能不會存取記憶體,或是程式因區段錯誤導致崩潰。一般情況下,雖然這種程式碼能寫得出來,但不會有任何好理由寫出它。
5555

5656
```rust
5757
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-02/src/main.rs:here}}
@@ -69,7 +69,7 @@ Rust 擁有另一個不安全的自我的另一理由是電腦硬體本質上就
6969

7070
建立一個指標沒有危險性,只有當我們嘗試存取它指向的值時,才可能需要處理非法的值。
7171

72-
請注意,範例 19-1 與 19-3,我們建立了 `*const i32``*mut i32` 兩個裸指標,皆指向相同儲存 `num` 的記憶體位置。若我們走正常程序建立指向 `num` 的不可變與可變參考,程式碼將因為 Rust 所有權規則不允許同時存在一個可變參考與多個不可變參考,進而無法編譯。有了裸指標,即可建立指向同個位置的可變指標和不可變指標,並透過可變指標改變其資料,但可能帶來資料競爭(data races),請小心!
72+
請注意,範例 19-1 與 19-3,我們建立了 `*const i32``*mut i32` 兩個裸指標,皆指向相同儲存 `num` 的記憶體位置。若我們走正常程序建立指向 `num` 的不可變與可變參考,程式碼將因為 Rust 所有權規則不允許同時存在一個可變參考與多個不可變參考,進而無法編譯。有了裸指標,即可建立指向同個位置的可變指標和不可變指標,並透過可變指標改變其資料,但可能帶來資料競爭(data races),請小心!
7373

7474
既然有這些危險,為什麼你還要用裸指標呢?一個主要使用案例是與 C 程式碼介接,你將會在下一節[「呼叫不安全函式或方法」](#呼叫不安全函式或方法)讀到。另一個用例是在借用檢查器不理解之處建立一層安全抽象。我們將會介紹不安全函式,再探討一個使用到不安全程式碼的安全抽象範例。
7575

@@ -135,9 +135,9 @@ Rust 的借用檢查器(borrow checker)不能理解我們同時借用一個
135135

136136
我們判定 `mid` 索引在該切片內。此後我們進入不安全程式碼:`slice::from_raw_parts_mut` 函式需要一個裸指標與一個長度,並建立一個切片。我們使用這個函式來建立一個從 `ptr` 開始長度為 `mid` 的切片。而後,我們以 `mid` 作為引數,對 `ptr` 呼叫 `add` 方法,以取得從 `mid` 開始的裸指標,再來用此指標與從 `mid` 開始剩下的元素個數作為長度,建立另一個切片。
137137

138-
`slice::from_raw_parts_mut` 之所以為不安全函式,是因為它需要裸指標,且必須相信這個指標合法`add` 是不安全方法是由於它必須相信偏移後的位址是合法指標。因此,我們需要在呼叫 `slice::from_raw_parts_mut``add` 外包一層 `unsafe` 函式。透過閱讀程式碼與加上對 `mid` 一定等於或比 `len` 小的斷言,我們可以宣稱所有在 `unsafe` 區塊的裸指標都是指向原始切片內的合法指標。這是一個可接受且合理的 `unsafe` 使用情境。
138+
`slice::from_raw_parts_mut` 之所以為不安全函式,是因為它需要裸指標,且必須相信這個指標有效`add` 是不安全方法是由於它必須相信偏移後的位址是有效指標。因此,我們需要在呼叫 `slice::from_raw_parts_mut``add` 外包一層 `unsafe` 函式。透過閱讀程式碼與加上對 `mid` 一定等於或比 `len` 小的斷言,我們可以宣稱所有在 `unsafe` 區塊的裸指標都是指向原始切片內的有效指標。這是一個可接受且合理的 `unsafe` 使用情境。
139139

140-
注意,我們不需替 `split_at_mut` 函式輸出結果做上 `unsafe` 的記號,而且我們可以在安全的 Rust 呼叫它。我們藉由安全的方式使用 `unsafe` 函式,完成了對不安全程式碼建立一層安全抽象,這個抽象只會從該函式能夠存取的資料內建立合法指標
140+
注意,我們不需替 `split_at_mut` 函式輸出結果做上 `unsafe` 的記號,而且我們可以在安全的 Rust 呼叫它。我們藉由安全的方式使用 `unsafe` 函式,完成了對不安全程式碼建立一層安全抽象,這個抽象只會從該函式能夠存取的資料內建立有效指標
141141

142142
對比之下,範例 19-7 中使用 `slice::from_raw_parts_mut` 則極有可能會在該切片被使用時崩潰。這段程式碼從任意的記憶體位置建立了一個 10,000 元素長的切片。
143143

@@ -147,7 +147,7 @@ Rust 的借用檢查器(borrow checker)不能理解我們同時借用一個
147147

148148
<span class="caption">範例 19-7:從任意記憶體位址建立切片</span>
149149

150-
我們不擁有此位址之下的記憶體,且並不保證這段程式碼建立的切片一定包含合法的 `i32` 值。嘗試將 `values` 當作合法的切片來使用,會導致為未定義行為(undefined behavior)。
150+
我們不擁有此位址之下的記憶體,且並不保證這段程式碼建立的切片一定包含有效的 `i32` 值。嘗試將 `values` 當作有效的切片來使用,會導致為未定義行為(undefined behavior)。
151151

152152
#### 使用 `extern` 函式呼叫外部程式碼
153153

src/ch19-03-advanced-types.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Rust 提供了替一個既有型別宣告型別別名的方式。對此我們會
2020
{{#rustdoc_include ../listings/ch19-advanced-features/no-listing-04-kilometers-alias/src/main.rs:here}}
2121
```
2222

23-
現在,`Kilometers` 別名就是 `i32`**同義詞**。不像我們在範例 19-15 建立的 `Millimeters``Meters` 型別,`Kilometers` 並非獨立的新型別。型別為 `Kilometers` 的值會被當作型別是 `i32` 的值。
23+
現在,`Kilometers` 別名就是 `i32`**同義詞**。不像我們在範例 19-15 建立的 `Millimeters``Meters` 型別,`Kilometers` 並非獨立的新型別。型別為 `Kilometers` 的值會被當作型別是 `i32` 的值。
2424

2525
```rust
2626
{{#rustdoc_include ../listings/ch19-advanced-features/no-listing-04-kilometers-alias/src/main.rs:there}}
@@ -110,8 +110,7 @@ Rust 有一個特殊的型別叫做 `!`,由於它沒有任何值,在型別
110110
{{#rustdoc_include ../listings/ch19-advanced-features/no-listing-09-unwrap-definition/src/lib.rs:here}}
111111
```
112112

113-
和範例 19-26 `match` 相同的情況,在這段程式碼再度上演:Rust 看到 `val` 的型別是 `T``panic``!` 型別,所以 `match` 表達式的總體結果是 `T`。這段程式碼可執行是因為 `panic!` 會結束程式而不會產生值。當遇上 `None` 的情形,我們不會從 `unwrap` 回傳任何值,所以這段程式碼合法有效。
114-
113+
和範例 19-26 `match` 相同的情況,在這段程式碼再度上演:Rust 看到 `val` 的型別是 `T``panic``!` 型別,所以 `match` 表達式的總體結果是 `T`。這段程式碼可執行是因為 `panic!` 會結束程式而不會產生值。當遇上 `None` 的情形,我們不會從 `unwrap` 回傳任何值,所以這段程式碼有效。
115114

116115
最後一個具有 `!` 型別的表達式是 `loop`
117116

src/ch19-05-macros.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ let v: Vec<u32> = vec![1, 2, 3];
4848

4949
我們的巨集定義從 `macro_rules!` 和我們欲定義的巨集名稱**去除**驚嘆號開始。這個名稱,在我們例子裡是 `vec`,的後面接著花括號表示巨集定義的本體。
5050

51-
這個 `vec!` 本體的結構和 `match` 表達式的結構相似。這裡我們有一個 match 分支,帶著模式 `( $( $x:expr ),* )`,並接著 `=>` 後面與該模式相關聯的程式碼區塊。這個分支是此巨集唯一一個模式,所以只有一個合法匹配方式;任何其他模式都會產生錯誤。更複雜的巨集會有多於一個分支。
51+
這個 `vec!` 本體的結構和 `match` 表達式的結構相似。這裡我們有一個 match 分支,帶著模式 `( $( $x:expr ),* )`,並接著 `=>` 後面與該模式相關聯的程式碼區塊。這個分支是此巨集唯一一個模式,所以只有一個有效匹配方式;任何其他模式都會產生錯誤。更複雜的巨集會有多於一個分支。
5252

53-
合法的巨集定義模式語法和在第十八章的模式語法並不相同,巨集的模式並不跟值比較,而是與 Rust 程式碼的結構相互匹配。在範例 19-28 我們會走過一次這些模式的意義,至於完整的巨集模式語法,請閱讀 [Rust 參考手冊]
53+
有效的巨集定義模式語法和在第十八章的模式語法並不相同,巨集的模式並不跟值比較,而是與 Rust 程式碼的結構相互匹配。在範例 19-28 我們會走過一次這些模式的意義,至於完整的巨集模式語法,請閱讀 [Rust 參考手冊]
5454

5555
首先,我們用一對括號包圍整個模式。我們使用錢字號(`$`)在巨集系統定義一個變數,該變數將包含與模式匹配的 Rust 程式碼。採用錢字號清楚展現它並非尋常的 Rust 變數,而是巨集變數。再來就是一對括號,用以捕獲與括號內的模式匹配之值,以替換為程式碼。在 `$()` 內的 `$x:expr` 會匹配任意 Rust 表達式,並賦予表達式一個 `$x` 名稱。
5656

0 commit comments

Comments
 (0)