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