为什么存在下划线前缀变量?
Why do underscore prefixed variables exist?
我在学习Rust,遇到在变量名开头加下划线,如果没有使用,编译器不会提示。我想知道为什么存在该功能,因为不使用未使用的变量是不受欢迎的。
我可以看出几个原因:
- 您正在调用一个 returns 类型为
#[must_use]
的函数,但在您的特定情况下,您知道可以安全地忽略该值。可以为此使用 _
模式(这不是变量绑定,它是自己的模式,但这可能是下划线前缀约定的来源),但您可能想要记录为什么忽略该值,或者该值是什么。根据我的经验,这在测试中尤为常见。
- 函数参数:您可能必须命名一个参数,因为它是您 API 的一部分,但实际上并不需要使用它。 Anonymous parameters were removed in the 2018 edition.
- 宏。在宏中创建的变量以后可能会或可能不会被使用。无法在宏调用中消除警告会很烦人。在这种情况下,有一个将下划线加倍的约定,例如由 clippy 的
used_underscore_binding
lint. 强制执行
- RAII。您可能希望为它的析构函数副作用而存在一个变量,但不要以其他方式使用它。对于这个用例,不可能简单地使用
_
,因为 _
不是变量绑定,并且不会像变量绑定那样在封闭块的末尾删除值。
以下是一些示例,说明您可能希望忽略未使用变量的行为的原因。考虑以下函数中的 _s
。
fn add_numbers(f: i32, _s: i32) -> i32 {
f + 1
}
_s
变量实现了这一点,因此即使我们没有实现它,我们也可以保持签名不变。如果我们发现不需要 _s
但由于我们的库用于许多不同的项目,我们不想将 API 更改为我们的函数,这也有效。这可能是也可能不是不好的做法,但在 _s
需要留下而不做任何事情的情况下可能很有用。我们也可以在这里使用 _
,但是 _s
可能对变量将来的用途有更多的意义。
下一个有用的地方是当类型实现 Drop
并且您关心该逻辑发生的位置时。在此示例中,您可以看到需要 _result
变量,以便 Drop
出现在最后。
fn main() {
let mut num = 1;
// let _ = try_add_numbers(&mut num); // Drop is called here for _
let _result = try_add_numbers(&mut num); // without the _result we have a warning.
println!("{}", num);
// Drop is called here for the _result
}
// keep the api the same even if an aurgument isn't needed anymore or
// has not been used yet.
fn add_numbers(f: i32, _s: i32) -> i32 {
f + 1
}
// This function returns a result
fn try_add_numbers(i: &mut i32) -> Result<GoodResult, GoodResult> {
if *i > 3 {
return Err(GoodResult(false));
}
*i = add_numbers(*i, 0);
Ok(GoodResult(true))
}
struct GoodResult(bool);
impl Drop for GoodResult {
fn drop(&mut self) {
let &mut GoodResult(result) = self;
if result {
println!("It worked");
} else {
println!("It failed");
}
}
}
如果我们使用 let _result = try_add_numbers(&mut num);
,我们有一个变量在范围内直到 main 结束,然后将调用 drop。如果我们使用了 let _ = try_add_numbers(&mut num);
,我们仍然不会收到警告,但会在语句末尾调用 drop 。如果我们在没有 let 绑定的情况下使用 try_add_numbers(&mut num);
,我们会收到警告。该程序的输出确实会根据我们在 try_add_numbers 函数中使用的内容而改变。
It worked
2
或
2
It worked
因此 _
和 _named
变量都有用处,需要根据程序的输出需要进行选择。试一试我在 playground 上的例子来感受一下。
我在查找与匹配变量相关的警告时偶然发现了这里 Google。这是切线相关的。
有时您可能会在代码中得到 Result
并希望匹配大小写,但您并不关心错误值。除了使用 _e
之类的东西,你实际上可以只使用 _
,它明确地不绑定。这是一个具体的例子。我们不关心错误的值,因为我们返回的是我们自己的值。
fn some_method() -> Result<u32, MyCustomError> {
// ...
let id: u32 = match some_str.parse() {
Ok(value) => value,
Err(_) => return Err(MyCustomError::Blah)
};
// ...
}
let a
是一个值绑定,会分配一个堆栈space来存储它的值。
let _a
的行为类似于 let a
。另外标记为intentional
,这样编译器在没有使用_a
时不会弹出警告。
let _
是一个模式,_
是一个reserved identifier
,不能在别处使用。这样不会导致分配一个栈space,所以=
右边的值会在这条语句后很快被释放
这里有一个例子:Playground
pub struct Node {
value: usize,
}
impl Drop for Node {
fn drop(&mut self) {
println!("drop() {}", self.value);
}
}
pub fn square() {
let a = Node { value: 1 };
let _a = Node { value: 2 };
let _ = Node { value: 3 };
println!("Hello, world!");
}
fn main() {
square();
}
输出是:
drop() 3
Hello, world!
drop() 2
drop() 1
您可以阅读 this 以了解更多信息
我在学习Rust,遇到在变量名开头加下划线,如果没有使用,编译器不会提示。我想知道为什么存在该功能,因为不使用未使用的变量是不受欢迎的。
我可以看出几个原因:
- 您正在调用一个 returns 类型为
#[must_use]
的函数,但在您的特定情况下,您知道可以安全地忽略该值。可以为此使用_
模式(这不是变量绑定,它是自己的模式,但这可能是下划线前缀约定的来源),但您可能想要记录为什么忽略该值,或者该值是什么。根据我的经验,这在测试中尤为常见。 - 函数参数:您可能必须命名一个参数,因为它是您 API 的一部分,但实际上并不需要使用它。 Anonymous parameters were removed in the 2018 edition.
- 宏。在宏中创建的变量以后可能会或可能不会被使用。无法在宏调用中消除警告会很烦人。在这种情况下,有一个将下划线加倍的约定,例如由 clippy 的
used_underscore_binding
lint. 强制执行
- RAII。您可能希望为它的析构函数副作用而存在一个变量,但不要以其他方式使用它。对于这个用例,不可能简单地使用
_
,因为_
不是变量绑定,并且不会像变量绑定那样在封闭块的末尾删除值。
以下是一些示例,说明您可能希望忽略未使用变量的行为的原因。考虑以下函数中的 _s
。
fn add_numbers(f: i32, _s: i32) -> i32 {
f + 1
}
_s
变量实现了这一点,因此即使我们没有实现它,我们也可以保持签名不变。如果我们发现不需要 _s
但由于我们的库用于许多不同的项目,我们不想将 API 更改为我们的函数,这也有效。这可能是也可能不是不好的做法,但在 _s
需要留下而不做任何事情的情况下可能很有用。我们也可以在这里使用 _
,但是 _s
可能对变量将来的用途有更多的意义。
下一个有用的地方是当类型实现 Drop
并且您关心该逻辑发生的位置时。在此示例中,您可以看到需要 _result
变量,以便 Drop
出现在最后。
fn main() {
let mut num = 1;
// let _ = try_add_numbers(&mut num); // Drop is called here for _
let _result = try_add_numbers(&mut num); // without the _result we have a warning.
println!("{}", num);
// Drop is called here for the _result
}
// keep the api the same even if an aurgument isn't needed anymore or
// has not been used yet.
fn add_numbers(f: i32, _s: i32) -> i32 {
f + 1
}
// This function returns a result
fn try_add_numbers(i: &mut i32) -> Result<GoodResult, GoodResult> {
if *i > 3 {
return Err(GoodResult(false));
}
*i = add_numbers(*i, 0);
Ok(GoodResult(true))
}
struct GoodResult(bool);
impl Drop for GoodResult {
fn drop(&mut self) {
let &mut GoodResult(result) = self;
if result {
println!("It worked");
} else {
println!("It failed");
}
}
}
如果我们使用 let _result = try_add_numbers(&mut num);
,我们有一个变量在范围内直到 main 结束,然后将调用 drop。如果我们使用了 let _ = try_add_numbers(&mut num);
,我们仍然不会收到警告,但会在语句末尾调用 drop 。如果我们在没有 let 绑定的情况下使用 try_add_numbers(&mut num);
,我们会收到警告。该程序的输出确实会根据我们在 try_add_numbers 函数中使用的内容而改变。
It worked
2
或
2
It worked
因此 _
和 _named
变量都有用处,需要根据程序的输出需要进行选择。试一试我在 playground 上的例子来感受一下。
我在查找与匹配变量相关的警告时偶然发现了这里 Google。这是切线相关的。
有时您可能会在代码中得到 Result
并希望匹配大小写,但您并不关心错误值。除了使用 _e
之类的东西,你实际上可以只使用 _
,它明确地不绑定。这是一个具体的例子。我们不关心错误的值,因为我们返回的是我们自己的值。
fn some_method() -> Result<u32, MyCustomError> {
// ...
let id: u32 = match some_str.parse() {
Ok(value) => value,
Err(_) => return Err(MyCustomError::Blah)
};
// ...
}
let a
是一个值绑定,会分配一个堆栈space来存储它的值。let _a
的行为类似于let a
。另外标记为intentional
,这样编译器在没有使用_a
时不会弹出警告。let _
是一个模式,_
是一个reserved identifier
,不能在别处使用。这样不会导致分配一个栈space,所以=
右边的值会在这条语句后很快被释放
这里有一个例子:Playground
pub struct Node {
value: usize,
}
impl Drop for Node {
fn drop(&mut self) {
println!("drop() {}", self.value);
}
}
pub fn square() {
let a = Node { value: 1 };
let _a = Node { value: 2 };
let _ = Node { value: 3 };
println!("Hello, world!");
}
fn main() {
square();
}
输出是:
drop() 3
Hello, world!
drop() 2
drop() 1
您可以阅读 this 以了解更多信息