屬性
你之前有見過 #[derive(Debug)]
這樣的程式碼:這種類型的程式碼叫做 屬性(Attribute)。這些屬性是能提供資訊給編譯器的小塊程式碼。它們雖然不容易建立,但使用起來非常方便。如果你只用 #
來寫屬性,那麼它將影響下一行的程式碼。但如果你是用 #!
來寫,那麼將影響它自己空間裡的一切。
這裡是一些你會經常見到的屬性:
#[allow(dead_code)]
和 #[allow(unused_variables)]
。如果你寫了用不到的程式碼,Rust 仍然會編譯,但會讓你知道。例如這裡是裡面什麼都沒有結構體和一個變數。它們任何一個我們都沒有用。
struct JustAStruct {} fn main() { let some_char = 'ん'; }
如果你這樣寫,Rust 會提醒你你沒有使用它們:
warning: unused variable: `some_char`
--> src\main.rs:4:9
|
4 | let some_char = 'ん';
| ^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_some_char`
|
= note: `#[warn(unused_variables)]` on by default
warning: struct is never constructed: `JustAStruct`
--> src\main.rs:1:8
|
1 | struct JustAStruct {}
| ^^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
我們知道可以在名字前面寫 _
,讓編譯器安靜下來:
struct _JustAStruct {} fn main() { let _some_char = 'ん'; }
但你也可以使用屬性。你會注意到在訊息中,它使用了 #[warn(unused_variables)]
和 #[warn(dead_code)]
。在我們的程式碼中,JustAStruct
是死程式碼(dead code),而 some_char
是個未使用的變數。warn
的反面是 allow
,所以我們可以這樣寫,它就不會再說什麼了:
#![allow(dead_code)] #![allow(unused_variables)] struct Struct1 {} // 做五個結構體 struct Struct2 {} struct Struct3 {} struct Struct4 {} struct Struct5 {} fn main() { let char1 = 'ん'; // 還有四個變數. 我們不使用它們任何一個但編譯器安靜了 let char2 = ';'; let some_str = "I'm just a regular &str"; let some_vec = vec!["I", "am", "just", "a", "vec"]; }
當然,處理死程式碼和未使用的變數是很重要的。但有時你希望編譯器安靜一段時間。或者是你可能需要展示一些程式碼或教人們 Rust,但又不想讓編譯器訊息來迷惑他們的時候。
#[derive(TraitName)]
讓你可以給你建立的結構和列舉推導出一些特徵。這適用於許多可以被自動推導的常見特徵。有些像 Display
這樣的特徵不能自動推導,因為對於 Display
,你必須選擇如何去顯示:
// ⚠️ #[derive(Display)] struct HoldsAString { the_string: String, } fn main() { let my_string = HoldsAString { the_string: "Here I am!".to_string(), }; }
錯誤訊息會告訴你:
error: cannot find derive macro `Display` in this scope
--> src\main.rs:2:10
|
2 | #[derive(Display)]
|
但是對於可以自動推匯出的特徵,你可以隨心所欲的放進去。讓我們在一行裡加入七個特徵給 HoldsAString
,當然只是為了好玩,儘管它只需要一個。
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone)] struct HoldsAString { the_string: String, } fn main() { let my_string = HoldsAString { the_string: "Here I am!".to_string(), }; println!("{:?}", my_string); }
另外,如果(也只有在)結構體的所有欄位都實作了 Copy
的情況下,你才可以讓結構體是 Copy
的。HoldsAString
裡的 String
不是 Copy
,所以你不能對它使用 #[derive(Copy)]
。但是對下面這個結構是可以的:
#[derive(Clone, Copy)] // 你也需要 Clone 來使用 Copy struct NumberAndBool { number: i32, // i32 是 Copy true_or_false: bool // bool 也是 Copy. 所以沒問題 } fn does_nothing(input: NumberAndBool) { } fn main() { let number_and_bool = NumberAndBool { number: 8, true_or_false: true }; does_nothing(number_and_bool); does_nothing(number_and_bool); // 如果它不能拷貝, 這裡會造成錯誤 }
#[cfg()]
的意思是組態,告訴編譯器是否執行程式碼。它通常是像這樣的:#[cfg(test)]
。你會在寫測試函式的時候用到,這樣它就知道不要執行它們除非你在跑測試。那麼你可以在你的程式碼附近寫測試,但編譯器不會執行它們,除非你告訴它這麼做。
另一個會使用 cfg
的例子是 #[cfg(target_os = "windows")]
。有了它,你可以告訴編譯器只能在 Windows 上執行程式碼,Linux 或其他平臺則不能。
#![no_std]
是個有趣的屬性,它告訴 Rust 不要引入標準函式庫。這表示你沒有 Vec
、String
以及標準函式庫中的其他任何東西可以用。你會在那些沒有多少記憶體或空間的小型裝置的程式碼中看到這個。
你可以在這裡看到更多的屬性。