What is the reason of using _
?
(_ , ... , ...)
_ = ...() ;
instead of:
(, ... , ...)
...() ;
Are there situations, where _
helps ?
What is the reason of using _
?
(_ , ... , ...)
_ = ...() ;
instead of:
(, ... , ...)
...() ;
Are there situations, where _
helps ?
It's unclear what your question is. _
is a placeholder for any type (in type declarations) or any (sub)pattern (in patterns). It's useful for instructing the compiler to infer a type or ignore a pattern, without having to specify all types and all subpatterns explicitly.
Thank you for response. I am talking: Instead of using:
_ = fn1();
in would be nice to use more elegant way:
fn1();
Calling fn1()
alone will trigger #[must_use]
warnings, if the function or its return type are annotated such. Assigning to _
is one way to suppress that.
This means "explicitly ignore this".
fn1
returns some type annotated with #[must_use]
like Iterator
implemented types, or Result
s or Option
s, etc. If I write fn1()
then the compiler infers that I've forgotten to deal with those values.
If I instead write let _ = fn1()
then I mean explicitly ignore this.
You definitely can call a function without assigning its return value to anything. This is already possible, in today's Rust.
This alternative would be ambiguous in patterns like Some(_)
vs Some()
.
_
is a special pattern in that it doesn't actually bind anything.[1] This effects the drop scope of things being matched on the right hand side.
// Doesn't bind the RHS to anything. Return value immediately drops.
// Doesn't count as a use for the sake of `#[must_use]`.
f();
// Binds the RHS to `variable`. Drop scope is the end of the block.
// Counts as a use.
let variable = f();
// Binds the RHS to `_variable`. Drop scope is the end of the block.
// Counts as a use. No warning if `_variable` itself is otherwise unused.
let _variable = f();
// *Doesn't bind the RHS to anything.* Return value immediately drops.
// Counts as a use.
let _ = f();
This can also be relevant in match
statements, where your _
pattern might be buried somewhere deeper.
It can be relevant when your drop location matters, like when locking a Mutex
.
This might give the false impression that
let _ = EXPRESSION;
and
EXPRESSION;
are always the same, besides the difference in #[must_use]
warnings.
That’s not the case.
EXPRESSION;
behaves more like
drop(EXPRESSION);
The difference becomes apparent when EXPRESSION
is a place expression. (For value expressions like f()
there really is no difference though )
let x = String::from("hello");
x; // drops x
// compilation error:
let x = String::from("hello");
x; // drops x
x; // cannot work twice
// compiles fine:
let x = String::from("hello");
let _ = x; // does nothing
// so let's do nothing again…
let _ = x; // does nothing
let _ = x; // does nothing
let _ = x; // does nothing
By the way… these expressions are a bit weird…, they quite literally act as if they weren’t there:
// compiles fine:
let mut x = String::from("hello");
let r = &mut x;
let _ = x; // yes, *really* does nothing
*r += " world";
println!("{x}");
let _ = r; // yes, *really* does nothing
drop(x);
let _ = x; // yes, *really* does nothing
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.