Rust is jet another language allowing assigning to _ for discarding assignment results (like function calls)
Posted by jpluimers on 2022/09/16
Rust is a programming language that very much emphasises safety.
One of the cornerstones is that it warns on constructs that might lose information or make object lifetime unclear.
An example is discarding function results: historically you will get a warning like warning: unused `std::result::Result` that must be used
that you can resolve by applying a let
statement assigning to the _
underscore pattern which will be discarded at the end of the statement like this:
let _ = foo();
By sheer coincidence, I bumped into [Wayback/Archive] David Tolnay on Twitter: “You know how when you ignore an error from a function because it’s okay it failed, and otherwise you get “warning: unused Result that must be used”, the past 7 years we’ve all been doing it like this?— let _ = foo();
Turns out since Rust 1.59 it can be just: _ = foo();
“.
I could not directly find this in the release notes, which is caused by the fact that – like in most programming languages – the naming of constructs usually is far less comprehensible than their actual practical usage. You see this both in the references below and (regrettably but unavoidably) in mu current blog post.
The practical solution now is much shorter:
_ = foo();
This turns the let
statement assigning to an underscore pattern into an underscore expression.
The compiler change that made this possible is centered around another programming language concept: destructuring assignment which allows to map assignments from one data structure to another data structure (in its simplest form from an array or structure to individual variables).
The below steps made me find the actual issue and commit adding the improvement, each step tracking deeper into the what, why and how:
- [Wayback/Archive] rust/RELEASES.md: version 1.590 (2022-02-24) at master · rust-lang/rust
- [Wayback/Archive] Stabilize `destructuring_assignment` by jhpratt · Pull Request #90521 · rust-lang/rust
- [Wayback/Archive] Tracking Issue for `destructuring_assignment` · Issue #71126 · rust-lang/rust
- [Wayback/Archive] Make `
_
` an expression, to discard values in destructuring assignments by fanzier · Pull Request #79016 · rust-lang/rust
With this PR, an underscore_
is parsed as an expression but is allowed only on the left-hand side of a destructuring assignment. There it simply discards a value, similarly to the wildcard_
in patterns. For instance,(a, _) = (1, 2)
will simply assign 1 toa
and discard the 2. Note that for consistency,_ = foo
is also allowed and equivalent to justfoo
. - [Wayback/Archive] Destructuring assignment by varkor · Pull Request #2909 · rust-lang/rfcs
We allow destructuring on assignment, as inlet
declarations. For instance, the following are now
accepted:(a, (b.x.y, c)) = (0, (1, 2)); (x, y, .., z) = (1.0, 2.0, 3.0, 4.0, 5.0); [_, f, *baz()] = foo(); [g, _, h, ..] = ['a', 'w', 'e', 's', 'o', 'm', 'e', '!']; Struct { x: a, y: b } = bar(); Struct { x, y } = Struct { x: 5, y: 6 };
This brings assignment in line withlet
declaration, in which destructuring is permitted. This will simplify and improve idiomatic code involving mutability. - [Wayback/Archive] Implement destructuring assignment by fanzier · Pull Request #71156 · rust-lang/rust
The cool thing about drilling deeper is that it taught me how the processes around maintaining the Rust repository are organised (especially the tracking issue).
Rust destructuring examples
Since rust goes further than simple destructuring, I added a few references to the documentation from [Wayback/Archive] Destructuring – Rust By Example:
- [Wayback/Archive] tuples – Rust By Example
- [Wayback/Archive] arrays/slices – Rust By Example
- [Wayback/Archive] enums – Rust By Example
- [Wayback/Archive] pointers/ref – Rust By Example
- [Wayback/Archive] structs – Rust By Example
The ecosystem
This new language constructs now leads for the need of surrounding tooling like lint tools or parsers to be updates just as mentioned in these issues:
- [Wayback/Archive] Parser doesn’t recognize destructuring assignment with underscore wildcard pattern · Issue #9274 · intellij-rust/intellij-rust.
- [Wayback/Archive] Using underscore to ignore elements in a destructuring tuple assignment is flagged as a syntax error · Issue #11454 · rust-lang/rust-analyzer
Similarly the community Rust wiki needs to be updated as it only reflects the former let
statement: [Wayback/Archive] Underscore – Rust Community Wiki
Underscores in
let
bindings[edit]Since
let
accepts a pattern,let _ =
can be used to ignore a#[must_use]
value such as aResult
in order to suppress compiler warnings.let _ = std::fs::remove_file("file.txt"); // Ignore the resultNote that it’s often better to unwrap the result instead, to make sure that errors aren’t silently discarded.
This is cool, as it means the ecosystem will quickly adopt the new language feature.
References
- [Wayback/Archive] rust/RELEASES.md: version 1.590 (2022-02-24) at master · rust-lang/rust
- [Wayback/Archive] Diagnostics: the
must_use
attribute – The Rust Reference - [Wayback/Archive] Statements:
let
statements – The Rust Reference - [Wayback/Archive] Patterns: wildcard pattern (
_
) – The Rust Reference - [Wayback/Archive] Underscore expressions (
_
) – The Rust Reference - [Wayback/Archive] David Tolnay on Twitter: “You know how when you ignore an error from a function because it’s okay it failed, and otherwise you get “warning: unused Result that must be used”, the past 7 years we’ve all been doing it like this?—
let _ = foo();
Turns out since Rust 1.59 it can be just:_ = foo();
“
Related
- [Wayback/Archive] What is the best way to ignore a `Result`? – help – The Rust Programming Language Forum
- [Wayback/Archive] rust – What is the proper way to ignore an error for a statement with no returned value? – Stack Overflow
- [Wayback/Archive] How do I ignore an error returned from a Rust function and proceed regardless? – Stack Overflow
- [Wayback/Archive] rust – Why am I getting “unused Result which must be used … Result may be an Err variant, which should be handled” even though I am handling it? – Stack Overflow
–jeroen
Leave a Reply