To reiterate a point already made in this thread: let _ = foo() does not ādrop stuff directlyā. It rather does ānot bindā anything which may result in dropping the value if it was only a temporary. The most natural way to think about let _ = EXPR; is: itās exactly the same as an EXPR; statement (and not the same as drop(EXPR)) except for the fact that let _ = EXPR; suppresses must_use warnings.
The reason for why let _ = EXPR; exists in the first place is rather for consistency since _ is an irrefutable pattern (which is of course much more useful in a match statement) and let supports all other kinds of irrefutable patterns, too.
As to why let _ and let _a must be different: Thatās because patterns donāt need to move the value they match on but they can also just reference them: Thereās ref PAT patterns and ref mut PAT patterns, so you can write something like
fn foo() {
struct S{field: (bool,T)};
impl Drop for S {
fn drop(&mut self) {}
}
struct T;
impl T {
fn bar(&self) {
println!("bar");
}
}
let s = S{field: (false,T)};
match s.field {
(true, ref t) => t.bar(),
_ => ()
}
}
The match statement here inspects the field of s without trying to move it (which would be illegal since S implements Drop). Thus the pattern _ cannot do any moving here. You want to always support a default-case with _ in a match like the above and you want {let PAT = EXPR; BODY} to be same as match EXPR { PAT => {BLOCK} }, this the behavior of let _.
There could is a point to be made that ref patterns are just as confusing as let _ and both should be forbidden in a change that makes let and match always move their value (unless it implements Copy, like when itās a reference, and with an automatic re-borrow inserted for mutable references). In this case, we could now choose to either have matching against _ behave like drop or like binding to some _a. let _ would either be useless now (when itās like drop -> so we can disallow it and suggest to just use drop) or it wouldnāt be confusing anymore (if it binds to _a).
But this isnāt going to happen anytime soon. One would also need to introduce an alternative mechanism for suppressing must_use, or, I suppose, one could use drop dropping must_use temporaries.
Huge language changes aside, something that we could probably do right now:
Iām not 100% sure on that, but wouldnāt let _ = EXPR; be the same as EXPR; in all the cases without a must_use warning, and the same as drop(EXPR) in all must_use cases? I suppose we could just add warnings against every let _ = EXPR; and either suggest EXPR; or drop(EXPR) for replacement depending on whether a must_use warning would be generated. In my opinion, if Iām not missing anything, this would make a lot of code a lot clearer.