Rust: Documet `let _ = ...` behavior saliently, or even warn about it

Created on 25 Feb 2017  路  10Comments  路  Source: rust-lang/rust

A friend ran into an issue today with the following code:

let _ = x.lock();

This apparently secures a lock and then immediately drops it, cf. #10488. The following code holds onto the lock instead of dropping it:

let _lock = x.lock();

I found this surprising and was wholly unaware of this special meaning of the _ name, and going by the reactions of @alexcrichton and others in #10488, I'm not the only one. It's not the most Google-able topic, but I wasn't able to find documentation of this except in the issue referenced above. I have to wonder how much Rust code out there has a landmine like this buried somewhere inside, undiscovered. Not the best look for a language aiming at safety and concurrency.

I suggest doing one of the following:

  1. Documenting this special behavior prominently, somewhere that isn't easy for newcomers to miss.
  2. Warn about uses of let _ = foo();. One could argue that all such lines should be replaced by the more clear foo();.
  3. The nuclear option: change the behavior of let _ = ... or remove it from the language entirely. Not likely post-stabilization and given that the previous discussion in #10488. However, I worry about Rust going the way of languages like C++ and accumulating a collection of misfeatures and "don't touch this" areas due to legacy reasons.
T-doc

Most helpful comment

hmm, that was hard to find, especially since it is under the section about starting a name with _ instead of the section about using just "_". Also it doesn't say anything about the behavior wrt drop.

All 10 comments

Warn about uses of let _ = foo();. One could argue that all such lines should be replaced by the more clear foo();.

let _ = ... is commonly used to suppress the must_use warning.

I think it should be documented. For me it was kind of obvious as it was consistent with general behavior of _, but if it confused you then there are many people who it confused as well.

FWIW, it is somewhat documented in the book:

It鈥檚 worth noting that using _ never binds the value in the first place, which means that the value does not move:

_[...code...]_

This also means that any temporary variables will be dropped at the end of the statement:

// Here, the String created will be dropped immediately, as it鈥檚 not bound:

let _ = String::from("  hello  ").trim();

Yes, this is already described in the book, and that section is linked to from the syntax index. Not much more we can do here, I don't think.

There are still some things you could do:

  1. Document underscore as a keyword, because it has special semantics. Self and self are already keywords.
  2. Change all sorts of syntax highlighters so they render _ in a different color.

As I understand _ has special semantics everywhere it is used. In patterns it does not bind a variable and in types it is a wildcard.

_ isn't a keyword, though.

Triage: this is already documented, closing!

Is this documented somewhere in version two of the book? I haven't been able to find it, and this caused me some frustration. At least not explicitly, maybe it would be possible to figure this out from "as a wildcard pattern that will match any value but not bind to the value", but it certainly isn't obvious and there isn't anything that calls out this potential footgun.

https://doc.rust-lang.org/stable/book/ch18-03-pattern-syntax.html?#ignoring-an-unused-variable-by-starting-its-name-with-_

Note that there is a subtle difference between using only _ and using a name that starts with an underscore. The syntax _x still binds the value to the variable, whereas _ doesn鈥檛 bind at all. To show a case where this distinction matters, Listing 18-21 will provide us with an error.

hmm, that was hard to find, especially since it is under the section about starting a name with _ instead of the section about using just "_". Also it doesn't say anything about the behavior wrt drop.

Was this page helpful?
0 / 5 - 0 ratings