8 files changed +10
-10
lines changed Original file line number Diff line number Diff line change @@ -18,7 +18,7 @@ fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
18
18
let gen = quote ! {
19
19
impl HelloMacro for #name {
20
20
fn hello_macro( ) {
21
- println!( "你好,巨集, 我叫做{}!" , stringify!( #name) ) ;
21
+ println!( "你好,巨集! 我叫做{}!" , stringify!( #name) ) ;
22
22
}
23
23
}
24
24
} ;
Original file line number Diff line number Diff line change @@ -17,7 +17,7 @@ fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
17
17
let gen = quote ! {
18
18
impl HelloMacro for #name {
19
19
fn hello_macro( ) {
20
- println!( "你好,巨集, 我叫做{}!" , stringify!( #name) ) ;
20
+ println!( "你好,巨集! 我叫做{}!" , stringify!( #name) ) ;
21
21
}
22
22
}
23
23
} ;
Original file line number Diff line number Diff line change 28
28
29
29
### 為型別實作特徵
30
30
31
- 現在我們已經用 ` Summary ` 特徵定義了所需的方法簽名。我們可以在我們多媒體聚集器的型別中實作它。範例 10-13 顯示了 ` NewsArticle ` 結構體實作 ` Summary ` 特徵的方式,其使用頭條、作者、位置來建立 ` summerize ` 的回傳值。至於結構體 ` Tweet ` ,我們使用使用者名稱加上整個推文的文字來定義 ` summarize ` ,因為推文的內容長度已經被限制在 280 個字元以內了。
31
+ 現在我們已經用 ` Summary ` 特徵定義了所需的方法簽名。我們可以在我們多媒體聚集器的型別中實作它。範例 10-13 顯示了 ` NewsArticle ` 結構體實作 ` Summary ` 特徵的方式,其使用頭條、作者、位置來建立 ` summarize ` 的回傳值。至於結構體 ` Tweet ` ,我們使用使用者名稱加上整個推文的文字來定義 ` summarize ` ,因為推文的內容長度已經被限制在 280 個字元以內了。
32
32
33
33
<span class =" filename " >檔案名稱:src/lib.rs</span >
34
34
Original file line number Diff line number Diff line change 1
1
## 參考循環會導致記憶體泄漏
2
2
3
- 意外情況下,執行程式時可能會產生永遠不會被清除的記憶體(通稱為** 記憶體泄漏/memory leak** )。Rust 的記憶體安全性雖然可以保證令這種情況難以發生,但並非絕不可能。雖然 Rust 在編譯時可以保證做到禁止資料競爭(** data races** ),但它無法保證完全避免記憶體泄漏,這是因為對 Rust 來說,記憶體泄漏是屬於安全範疇內的(** memory safe** ) 。透過使用 ` Rc<T> ` 和 ` RefCell<T> ` ,我們能觀察到 Rust 允許使用者自行產生記憶體泄漏:因為使用者可以產生兩個參考並互相參照,造成一個循環。這種情況下會導致記憶體泄漏,因為循環中的參考計數永遠不會變成 0,所以數值永遠不會被釋放。
3
+ 意外情況下,執行程式時可能會產生永遠不會被清除的記憶體(通稱為** 記憶體泄漏/memory leak** )。Rust 的記憶體安全性雖然可以保證令這種情況難以發生,但並非絕不可能。雖然 Rust 在編譯時可以保證做到禁止資料競爭(** data races** ),但它無法保證完全避免記憶體泄漏,這是因為對 Rust 來說,記憶體泄漏是屬於安全範疇內的(** memory safe** ) 。透過使用 ` Rc<T> ` 和 ` RefCell<T> ` ,我們能觀察到 Rust 允許使用者自行產生記憶體泄漏:因為使用者可以產生兩個參考並互相參照,造成一個循環。這種情況下會導致記憶體泄漏,因為循環中的參考計數永遠不會變成 0,所以數值永遠不會被釋放。
4
4
5
5
### 產生參考循環
6
6
Original file line number Diff line number Diff line change @@ -209,7 +209,7 @@ Rust 的全域變數稱做**靜態**變數。範例 19-9 展示了宣告並使
209
209
210
210
<span class =" caption " >範例 19-10:讀取與寫入可變的靜態變數為不安全的操作</span >
211
211
212
- 與普通變數一樣,我們透過 ` mut ` 關鍵字指明可變性。任何讀寫 ` COURTER ` 的程式碼皆必須在 ` unsafe ` 區塊中。這個程式碼會編譯並打印出我們預期中的 ` COUNTER: 3 ` 是因為他在單執行緒執行,若在多執行緒存取 ` COUTER ` 則可能導致資料競爭。
212
+ 與普通變數一樣,我們透過 ` mut ` 關鍵字指明可變性。任何讀寫 ` COURTER ` 的程式碼皆必須在 ` unsafe ` 區塊中。這個程式碼會編譯並打印出我們預期中的 ` COUNTER: 3 ` 是因為他在單執行緒執行,若在多執行緒存取 ` COUNTER ` 則可能導致資料競爭。
213
213
214
214
當能從全域存取可變資料時,確保沒有資料競爭就不容易了,這就是為什麼 Rust 將可變的靜態變數視為不安全。若是可能的話,我們推薦使用第十六章討論的並行技術與執行緒安全(thread-safe)的智慧指標(smart pointer),如此一來編譯器就能檢查從不同執行緒存取資料是安全的。
215
215
Original file line number Diff line number Diff line change @@ -262,7 +262,7 @@ Rust 並沒有限制不同特徵之間不能有同名的方法,也沒有阻止
262
262
263
263
因為 ` Wrapper ` 是一個元組結構體而 ` Vec<T> ` 是該元組在索引 0 上的項目,所以該 ` Display ` 的實作使用 ` self.0 ` 存取內部的 ` Vec<T> ` 。我們就可以在 ` Wrapper ` 上使用 ` Display ` 的功能了。
264
264
265
- 使用這個技術的缺點是 ` Wrapper ` 是個新型別,並無它封裝的值所擁有的方法。我們不得不在 ` Wapper ` 上實作所有 ` Vec<T> ` 的方法,委派這些方法給 ` self.0 ` ,讓我們可以將 ` Wrapper ` 作為 ` Vec<T> ` 一樣對待。如果我們想要新型別得到所有內部型別擁有的所有方法,一個解法是透過對 ` Wrapper ` 實作 ` Deref ` 特徵(在第十五章[ 「透過 ` Deref ` 特徵將智慧指標視為一般參考」] [ 智慧指標取值 ] 一節有相應討論)並回傳內部型別。如果我們不想要 ` Wrapper ` 擁有所有內部型別的方法,例如限制 ` Wrapper ` 型別之行為,就僅須實作那些我們想要的方法。
265
+ 使用這個技術的缺點是 ` Wrapper ` 是個新型別,並無它封裝的值所擁有的方法。我們不得不在 ` Wrapper ` 上實作所有 ` Vec<T> ` 的方法,委派這些方法給 ` self.0 ` ,讓我們可以將 ` Wrapper ` 作為 ` Vec<T> ` 一樣對待。如果我們想要新型別得到所有內部型別擁有的所有方法,一個解法是透過對 ` Wrapper ` 實作 ` Deref ` 特徵(在第十五章[ 「透過 ` Deref ` 特徵將智慧指標視為一般參考」] [ 智慧指標取值 ] 一節有相應討論)並回傳內部型別。如果我們不想要 ` Wrapper ` 擁有所有內部型別的方法,例如限制 ` Wrapper ` 型別之行為,就僅須實作那些我們想要的方法。
266
266
267
267
現在,你知道如何將新型別模式與特徵相關聯,縱使不涉及特徵,新型別模式仍非常實用。接下來我們將目光轉移到其他與 Rust 型別系統互動的方法吧。
268
268
Original file line number Diff line number Diff line change @@ -96,7 +96,7 @@ Rust 有一個特殊的型別叫做 `!`,由於它沒有任何值,在型別
96
96
{{#rustdoc_include ../listings/ch19-advanced-features/no-listing-08-match-arms-different-types/src/main.rs:here}}
97
97
```
98
98
99
- 這段程式碼中 ` guess ` 的型別必須是** 同時是** 整數與字串,並且 Rust 要求 ` guess ` 只能是一種型別。那 ` contiunue ` 回傳了什麼?範例 19-26 中,為什麼允許一個分支回傳 ` u32 ` 但同時有另一分支結束在 ` continue ` ?
99
+ 這段程式碼中 ` guess ` 的型別必須是** 同時是** 整數與字串,並且 Rust 要求 ` guess ` 只能是一種型別。那 ` continue ` 回傳了什麼?範例 19-26 中,為什麼允許一個分支回傳 ` u32 ` 但同時有另一分支結束在 ` continue ` ?
100
100
101
101
102
102
如你所猜,` continue ` 具有 ` ! ` 值。意即當 Rust 根據兩個分支來推算 ` guess ` 型別時,會觀察到前者會是 ` u32 ` ,而後者是 ` ! ` 。因為 ` ! ` 永遠不會有值,Rust 於是決定 ` guess ` 的型別為 ` u32 ` 。
@@ -123,7 +123,7 @@ Rust 有一個特殊的型別叫做 `!`,由於它沒有任何值,在型別
123
123
124
124
### 動態大小型別與 ` Sized ` 特徵
125
125
126
- Rust 需要了解其型別的特定細節,例如需替特定型別之值配置多少空間。這導致型別系統有令人困惑的小地方:即是** 動態大小型別** (dynamically sized type)的概念。有時稱為 * DST* 或** 不定大小(unsize )型別** ,這些型別賦予我們寫出僅能在執行期(runtime)得知值的大小之程式碼。
126
+ Rust 需要了解其型別的特定細節,例如需替特定型別之值配置多少空間。這導致型別系統有令人困惑的小地方:即是** 動態大小型別** (dynamically sized type)的概念。有時稱為 * DST* 或** 不定大小(unsized )型別** ,這些型別賦予我們寫出僅能在執行期(runtime)得知值的大小之程式碼。
127
127
128
128
讓我們深入研究一個貫穿全書到處使用的動態大小型別 ` str ` 的細節。你沒看錯,不是 ` &str ` 而是 ` str ` 本身就是 DST。在執行期前我們無從得知字串多長,也就表示無法建立一個型別為 ` str ` 的變數,更不能將 ` str ` 型別作為引數。試想以下不能執行的程式碼:
129
129
Original file line number Diff line number Diff line change @@ -98,7 +98,7 @@ pub fn some_name(input: TokenStream) -> TokenStream {
98
98
99
99
### 如何撰寫自訂的 ` derive ` 巨集
100
100
101
- 我們建立一個 ` hello_macro ` crate,並定義 ` HelloMacro ` 特徵與它的 ` hello_macro ` 關聯函式。我們提供一個程序式巨集,讓 crate 的使用者透過 ` #[derive(HelloMacro)] ` 標註它們的型別,來獲得預設的 ` hello_macro ` 函式的實作,而不需要使用者替每個型別手動實作 ` HelloMacro ` 特徵。這個預設的函式實作會印出 ` 你好,巨集, 我叫做型別名稱! ` ,其中 ` 型別名稱 ` 是實作特徵那個型別的名字。換句話說,就是我們會寫出一個 crate,讓其他程式設計師用我們的 crate,以範例 19-30 的方式來寫程式。
101
+ 我們建立一個 ` hello_macro ` crate,並定義 ` HelloMacro ` 特徵與它的 ` hello_macro ` 關聯函式。我們提供一個程序式巨集,讓 crate 的使用者透過 ` #[derive(HelloMacro)] ` 標註它們的型別,來獲得預設的 ` hello_macro ` 函式的實作,而不需要使用者替每個型別手動實作 ` HelloMacro ` 特徵。這個預設的函式實作會印出 ` 你好,巨集! 我叫做型別名稱! ` ,其中 ` 型別名稱 ` 是實作特徵那個型別的名字。換句話說,就是我們會寫出一個 crate,讓其他程式設計師用我們的 crate,以範例 19-30 的方式來寫程式。
102
102
103
103
<span class =" filename " >檔案名稱:src/main.rs</span >
104
104
@@ -212,7 +212,7 @@ DeriveInput {
212
212
213
213
` quote! ` 巨集也提供非常炫的模板機制:我們可以輸入 ` #name ` ,而 ` quote! ` 會以變數 ` name ` 值取而代之。我們甚至可以做一些類似普通巨集的重複工作。閱讀 [ ` quote ` crate 的文件] [ quote-docs ] 以獲得完整的介紹。
214
214
215
- 我們想要我們的程序式巨集對使用者標註的型別產生 ` HelloMacro ` 特徵的實作,這個標註的型別名稱可以從 ` #name ` 取得。這個特徵的實作有一個函式 ` hello_macro ` ,函式本體包含我們想要的功能:印出 ` 你好,巨集, 我叫做 ` 再加上被標註的型別的名稱。
215
+ 我們想要我們的程序式巨集對使用者標註的型別產生 ` HelloMacro ` 特徵的實作,這個標註的型別名稱可以從 ` #name ` 取得。這個特徵的實作有一個函式 ` hello_macro ` ,函式本體包含我們想要的功能:印出 ` 你好,巨集! 我叫做 ` 再加上被標註的型別的名稱。
216
216
217
217
` stringify! ` 巨集是 Rust 內建的,會將一個 Rust 表達式,例如 ` 1 + 2 ` ,在編譯期轉換成字串字面值(string literal),例如 ` "1 + 2" ` 。這和 ` format! ` 或 ` println! ` 巨集會對表達式求值並將結果轉為 ` String ` 不同。因為輸入的 ` #name ` 可能是一個表達式,但要直接照字面印出來,所以我們選擇使用 ` stringify! ` 。使用 ` stringify! ` 也可以節省在編譯器因為轉換 ` #name ` 成為字串字面量所需的空間配置。
218
218
0 commit comments