Destructuring closure parameters


#1

I’m new to rust, and I’m not sure if the following is valid syntax or not:

|(a, b)| a < b

Is it possible to destructure inside of a closure parameter like this? If not, why does the compiler give such an unhelpful error message?

   |
14 |     let (roman, arabic) = *CONVERSIONS.iter().find(|(x, y)| n >= y).unwrap();
   |                                                     ^^^^^^ expected reference, found tuple
   |
   = note: expected type `&&(&str, u16)`
   = note:    found type `(_, _)`

I’ve tried it with (multiple) & and * in front, but to no avail.


#2

The short answer is this find(|&&(x, y)| n >= y). As the error ultimately says, find is expecting that the closure takes an &&(&str, u16), but you are destructuring it as a (&str, u16).

Here’s why find expects a double-referenced tuple instead of just a tuple:

  1. The first reference: I’m not sure what type CONVERSIONS is, but I’m guessing its a &'static [(&str, u16)]; its iter() method constructs an iterator of &(&str, u16), not an iter of (&str, u16), because iter iterates by reference, rather than by move (you can’t move values out of a static).
  2. The find method takes a closure for which the argument is &Self::Item, this is because find wants to look at each value in the iterator without actually moving it. since the iter’s item is &(&str, u16), &Self::Item is &&(&str, u16).

#3

Thanks. I thought I had tried that syntax with the double ref, but apparently I had it wrong. Couldn’t find the combination of closures and destructuring documented anywhere.

(And, you were correct about the type signature of CONVERSIONS. I was working on the Roman Numeral Kata, for those interested)


#4

I almost always get an error because I forget that find over a by-ref iterator will have a double reference