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.