So I find myself writing this match statement:
line.push(match ch {
'a'..='z' => { (ch as u8) - ('a' as u8) }
'S' => { start = Some(UVec2::new(line.len() as u32, match grid { None => 0, Some(ref grid) => grid.nrows() as u32 })); 0 }
'E' => { end = Some(UVec2::new(line.len() as u32, match grid { None => 0, Some(ref grid) => grid.nrows() as u32 })); 25 }
_ => return invalid2()
})
The idea is that for a lowercase letter I produce its alphabet value, and for the special characters 'S' and 'E' I also save the current "grid position", where the "grid position" is this kinda hairy expression.
I really don't like this code repetition between 'S' and 'E'. So I do what I would do in any other language with closures, which is I write a helper function for constructing the "grid position":
line.push({
let at = Some(UVec2::new(line.len() as u32, match grid { None => 0, Some(ref grid) => grid.nrows() as u32 }));
match ch {
'a'..='z' => { (ch as u8) - ('a' as u8) }
'S' => { start = at(); 0 }
'E' => { end = at(); 25 }
_ => return invalid2()
}
})
And this works fine! So my problem's solved. But I still feel like I'm missing something about Rust ergonomics/idioms.
Because the problem with this is for all code inside the outer {}— in other words, all match branches— both line and grid are borrowed by the at
closure.
Let's imagine, hypothetically, I had to add another special character, 'G', which does... something complicated with line
, which requires line to be mutably borrowed by the 'G' match block. Now I can't use my
at()anymore because
atborrows
grid` and you can't have a mutable borrow, such as the one I will need in this hypothetical 'G' branch, if other borrows exist.
How would a normal Rust programmer solve this situation? I keep running into situations where a closure I wrote purely for my own convenience implicitly captures, borrows, and breaks things.
The only things I can think of are:
- instead of capturing
grid
and/orline
in at, pass them in as an argument— which if instead of "at" capturing two variables it captured nine, would get tiresome real fast, or - Do 'S' | 'E', which does work, but then I'm going to have to put a second inner
match
orif
testing onch
right after I've already matched it once, which is in its way also ugly. - A macro?? I guess??
It seems like either I'm missing something about how to write cleanly structured Rust, or people who write a lot of Rust just tolerate a higher level of mild repetition (EG repeatedly calling a function with the same nine parameters or repeatedly indexing into a complex structure with the same four indexes in the same order) than I'm used to.