Reputation: 38218
I'd like to destructure a tuple and assign part of the result to a new variable and assign another part of the result to an existing.
The following code illustrates the intent (it's a dumb example which results in an infinite loop printing [0]
):
fn main() {
let mut list = &[0, 1, 2, 3][..];
while !list.is_empty() {
let (head, list) = list.split_at(1);
// An obvious workaround here is to introduce a new variable in the above
// let statement, and then just assign it to list.
println!("{:?}", head);
}
}
This code creates a new variable list
instead of reassigning it.
If I change the code to the following (to avoid the let
that introduces the new list
variable), it doesn't compile:
fn main() {
let mut list = &[0, 1, 2, 3][..];
while !list.is_empty() {
let head;
(head, list) = list.split_at(1);
println!("{:?}", head);
}
}
Compilation error:
error[E0070]: invalid left-hand side of assignment
--> src/main.rs:5:22
|
5 | (head, list) = list.split_at(1);
| ------------ ^
| |
| cannot assign to this expression
|
Is there a way to do this, or can destructuring only be used in let
, match
, and for
statements?
Upvotes: 34
Views: 14290
Reputation: 11477
Yes.
The Rust team has published a new version of Rust 1.59.0 in Feb. 24, 2022, you can now use tuple, slice, and struct patterns as the left-hand side of an assignment.
Destructuring assignments
You can now use tuple, slice, and struct patterns as the left-hand side of an assignment.
let (a, b, c, d, e); (a, b) = (1, 2); [c, .., d, _] = [1, 2, 3, 4, 5]; Struct { e, .. } = Struct { e: 5, f: 3 }; assert_eq!([1, 2, 1, 4, 5], [a, b, c, d, e]);
This makes assignment more consistent with let bindings, which have long supported the same thing. Note that destructuring assignments with operators such as += are not allowed.
Before 1.59.0, you can only destructure it in Nightly version with #![feature(destructuring_assignment)]
.
Now you can do this trick in stable version and remove the feature line.
See more details from rust-lang/rust/issues/71126 and rust-lang/rust/pull/90521.
Upvotes: 25
Reputation: 430673
Nightly Rust has feature(destructuring_assignment)
, which allows your original attempt to compile as-is:
#![feature(destructuring_assignment)]
fn main() {
let mut list = &[0, 1, 2, 3][..];
while !list.is_empty() {
let head;
(head, list) = list.split_at(1);
println!("{:?}", head);
}
}
[0]
[1]
[2]
[3]
However, I'd solve this using stable features like slice pattern matching, which avoids the need for the double check in split_at
and is_empty
:
fn main() {
let mut list = &[0, 1, 2, 3][..];
while let [head, rest @ ..] = list {
println!("{:?}", head);
list = rest;
}
}
See also:
Upvotes: 4
Reputation: 58995
No.
Destructuring is something you can only do with patterns; the left-hand side of an assignment is not a pattern, hence you can't destructure-and-assign.
See proto-RFC 372 (Destructuring assignment) which discusses the possibility of adding this feature.
Upvotes: 19