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

Sir*_*pus 12 variables rust unused-variables

我正在学习Rust,并且发现在变量名的开头添加下划线将使编译器在未使用时不会发出警告.我想知道为什么这个功能存在,因为未使用的变量是不受欢迎的.

mca*_*ton 14

我可以看到几个原因:

  • 您正在调用返回#[must_use]类型的函数,但在您的特定情况下,您知道可以安全地忽略该值.可以使用一个_模式(它不是变量绑定,它是它自己的模式,但这可能是下划线前缀约定来自的地方),但您可能想要记录忽略该值的原因,或者那是什么价值.根据我的经验,这在测试中尤为常见.
  • 宏.在宏中创建的变量可能会或可能不会在以后使用.如果不能在宏调用中使警告静音,那将是令人讨厌的.在这种情况下,存在将下划线加倍的惯例,例如通过clippy的used_underscore_bindinglint 强制执行.
  • RAII.您可能希望为其析构函数副作用存在一个变量,否则不会使用它.不可能仅仅_为此用例使用,因为_它不是变量绑定,并且该值将在语句的末尾被删除.

  • *可以使用_模式来表示*`let _ =`和`let _foo =`的行为略有不同.两者都将停止警告,但是`let _`它将导致返回的值在语句的末尾而不是在范围的末尾被"丢弃",因为没有绑定来保存它. (4认同)
  • 接下来的字面意思是“这不是变量绑定”。但我在“RAII”点中添加了此评论,以进行澄清。 (2认同)

小智 8

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

这是一个例子:游乐场

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();
}
Run Code Online (Sandbox Code Playgroud)

输出是:

drop() 3
Hello, world!
drop() 2
drop() 1
Run Code Online (Sandbox Code Playgroud)

您可以阅读本文以了解更多信息


小智 5

以下是一些示例,说明为什么您可能想要忽略未使用变量的行为。_s在下面的函数中考虑。

fn add_numbers(f: i32, _s: i32) -> i32 {
    f + 1
}
Run Code Online (Sandbox Code Playgroud)

_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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我们使用let _result = try_add_numbers(&mut num);我们有一个变量,该变量在 main 结束之前一直在作用域内,然后将调用 drop。如果我们使用过,let _ = try_add_numbers(&mut num);我们仍然不会收到警告,但在语句的末尾会调用 drop。如果我们在try_add_numbers(&mut num);没有 let 绑定的情况下使用,我们会收到警告。该程序的输出确实会根据我们与 try_add_numbers 函数一起使用的内容而变化。

fn add_numbers(f: i32, _s: i32) -> i32 {
    f + 1
}
Run Code Online (Sandbox Code Playgroud)

或者

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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,需要根据程序的输出需要选择__named变量。在操场上玩我的例子来感受一下。

  • 我认为在 `Drop` 的情况下,手动调用 `drop` 比使用 `_result` 来隐藏 `_result` 实际用于某事更有意义。在需要时调用 `drop` 的情况下,它会使值被使用并且仍然很明显它需要被丢弃在某个地方。 (2认同)