rust 为什么有下划线前缀的变量存在?

jhiyze9q  于 2023-05-29  发布在  其他
关注(0)|答案(5)|浏览(335)

我正在学习Rust,并且遇到了这样一个事实,即在变量名的开头添加下划线将使编译器在未使用时不会发出警告。我想知道为什么存在这个特性,因为未使用的变量是不受欢迎的。

z0qdvdin

z0qdvdin1#

我可以看到几个原因:

  • 您正在调用一个返回#[must_use]类型的函数,但在您的特定情况下,您知道可以安全地忽略该值。可以使用_模式(这不是变量绑定,它是它自己的一种模式,但这可能是下划线前缀约定的来源),但您可能希望记录为什么忽略该值,或者该值是什么。在我的经验中,这在测试中特别常见。
  • 功能参数:您可能必须命名一个参数,因为它是API的一部分,但实际上并不需要使用它。Anonymous parameters were removed in the 2018 edition.
  • 宏。在宏中创建的变量以后可能会使用,也可能不会使用。如果不能使宏调用中的警告静音,那将是很烦人的。在这种情况下,有一个双下划线的约定,例如clippy的used_underscore_binding lint。
  • 你可能希望有一个变量存在的析构函数的副作用,但不使用它,否则。在这种情况下,不可能简单地使用_,因为_不是变量绑定,并且值不会像变量绑定那样在封闭块的末尾被删除。
gev0vcfq

gev0vcfq2#

下面是一些示例,说明为什么您可能需要忽略未使用的变量的行为。在下面的函数中考虑_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上试用我的示例来感受一下。

y53ybaqx

y53ybaqx3#

  • let a是一个值绑定,将分配堆栈空间来存储它的值。
  • let _a的行为类似于let a。此外,它被标记为intentional,这样如果没有使用_a,编译器就不会弹出警告。
  • let _是一个模式,_是一个不能在其他地方使用的reserved identifier。这不会导致分配堆栈空间,因此=右侧的值将在此语句后立即释放。

下面是一个例子: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以了解更多信息

xwbd5t1u

xwbd5t1u4#

我在谷歌上偶然发现了这个与匹配变量相关的警告。这是间接相关的。
有时候,您可能会在代码中得到一个Result,并希望匹配大小写,但您并不关心错误值。实际上,您可以使用显式不绑定的_,而不是使用_e或其他东西。这里有一个具体的例子。我们不关心错误的值,因为我们返回自己的值。

fn some_method() -> Result<u32, MyCustomError> {
    // ...
    let id: u32 = match some_str.parse() {
        Ok(value) => value,
        Err(_) => return Err(MyCustomError::Blah)
    };
    // ...
}
wmomyfyw

wmomyfyw5#

只需定义变量的类型即
例如:

fn names(){ //this case the function gives an warning like "help: if this is intentional, prefix it with an underscore: `_myname`"
      let myname = "yash";  
      }

因此,通过对代码的最新更改,您可以克服此错误:

fn name(){ //here warning won't come
     let myname: &str = "bash"
       }

相关问题