53-属性
属性 — Easy Rust 中文译本
译文 · 基于 Easy Rust
原文链接: https://dhghomon.github.io/easy_rust/Chapter_52.html
中文参考:kumakichi/easy_rust_chs
属性
你以前见过#[derive(Debug)]这样的代码:这种类型的代码叫做属性。这些属性是给编译器提供信息的小段代码。它们不容易创建,但使用起来非常方便。如果你只用#写一个属性,那么它将影响下一行的代码。但如果你用#!来写,那么它将影响自己空间里的一切。
下面是一些你会经常看到的属性。
#[allow(dead_code)] 和 #[allow(unused_variables)]。 如果你写了不用的代码,Rust仍然会编译,但会让你知道。例如,这里有一个结构体,里面什么都没有,只有一个变量。我们不使用它们中的任何一个。
1
2
3
4
5
| struct JustAStruct {}
fn main() {
let some_char = 'ん';
}
|
如果你这样写,Rust会提醒你,你没有使用它们。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| 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
|
我们知道,可以在名字前写一个_,让编译器安静下来。
1
2
3
4
5
| struct _JustAStruct {}
fn main() {
let _some_char = 'ん';
}
|
但你也可以使用属性。你会注意到在消息中,它使用了#[warn(unused_variables)]和#[warn(dead_code)]。在我们的代码中,JustAStruct是死代码,而some_char是一个未使用的变量。warn的反义词是allow,所以我们可以这样写,它不会说什么。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| #![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)]让你可以为你创建的结构和枚举派生一些trait。这适用于许多可以自动派生的常见trait。有些像 Display 这样的特性不能自动衍生,因为对于 Display,你必须选择如何显示。
1
2
3
4
5
6
7
8
9
10
11
| // ⚠️
#[derive(Display)]
struct HoldsAString {
the_string: String,
}
fn main() {
let my_string = HoldsAString {
the_string: "Here I am!".to_string(),
};
}
|
错误信息会告诉你:
1
2
3
4
5
| error: cannot find derive macro `Display` in this scope
--> src\main.rs:2:10
|
2 | #[derive(Display)]
|
|
但是对于可以自动推导出的trait,你可以随心所欲的放进去。让我们给HoldsAString在一行中加入七个trait,只是为了好玩,尽管它只需要一个。
1
2
3
4
5
6
7
8
9
10
11
| #[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)]。但是对下面这个结构你可以:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| #[derive(Clone, Copy)] // 要用 Copy,还需要 Clone
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); // 如果没有 Copy,这里会报错
}
|
#[cfg()]的意思是配置,告诉编译器是否运行代码。它通常是这样的:#[cfg(test)]。你在写测试函数的时候用这个,这样它就知道除非你在测试,否则不要运行它们。那么你可以在你的代码附近写测试,但编译器不会运行它们,除非你告诉编译器。
还有一个使用cfg的例子是#[cfg(target_os = "windows")]。有了它,你可以告诉编译器只在Windows,Linux或其他平台则不能运行代码。
#![no_std]是一个有趣的属性,它告诉Rust不要引入标准库。这意味着你没有Vec,String,以及标准库中的其他任何东西。你会在那些没有多少内存或空间的小型设备的代码中看到这个。
你可以在这里看到更多的属性。