Why extracting values from tuple to existing variables so hard?


#1

After I start programming on Rust I often meet such restriction as in this code:

fn return_tuple() -> (Point, i32);

fn f(a: Option<i32>, base_point: Point) {
  let mut point = base_point;
  let mut some_val = 17;
  match a {
     Some(val) => {
        if (val > 5) {
          (point, some_val) = return_tuple();
        }
     }
     None => { /*somthing else */
  }
}

As you can see in function f I use variables point and some_val,
basically I use this variables “as is”, but if several conditions match, I need
rewrite this variables with values from return_tuple.

And here is problem, I can NOT just write (point, some_val) = return_tuple(),
I have to use match, or introduce new local variables and then assign
their values to point and some_val.

Is any way to simplify extraction values from tuple?


#2

It’s a sufficiently common need.


#3

If you want to avoid new local variables you can just write s.th. like this (untested):

 let (point, some_val) = match a {
     Some(val) if (val > 5) => return_tuple()
     Some(val) => // s.th. else if val <= 5
     None => (base_point, 17)
  }

More information here: https://doc.rust-lang.org/book/patterns.html


#4

The grammar for let statement looks like let pattern = expression. The grammar for assignment expression is expression = expression. What you’re proposing is adding a special case – when LHS of assignment is a tuple literal, treat the whole thing differently. This also changes the whole thing from being an expression to a statement. This special case will make Rust grammar more complicated, which may be a problem for external tools and rustc itself, as parsing with that change requires arbitrary lookahead.

Lack of this feature is pointed out quite often, but it’s not that annoying, as it seems – it turns out it’s usually necessary only in loops (eg. fibonacci calculation can contain a line (a, b) = (b, a + b)). For example your snippet could be rewritten as:

fn f(a: Option<i32>, base_point: Point) {
  let (base_point, some_val) = match a {
     Some(val) if val > 5 => return_tuple(),
     None => (base_point, 17),
  }
}

If you feel that you really need that feature, you could always make a macro, or create an extension trait for tuples, so you could write:

assign_tuple!(base_point, val = return_tuple());
// or
return_tuple().unpack_to(&mut base_point, &mut val);