There's a relatively frequent annoying problem with closures in Rust, which I haven't seen discussed a lot. I am wondering if I miss any relevant discussions?
The problem comes up when using closures to abstract some code repetition locally.
For example, let's say you have:
let mut acc: Vec<Result<Item, Error>> = Vec::new();
then, you have a bunch of code that adds Ok
values there:
let mut acc: Vec<Result<Item, Error>> = Vec::new();
if cond1 {
acc.push(Ok(x1));
acc.push(Ok(x2));
}
if cond2 {
acc.push(Ok(y));
}
You can abstract over that with a closuer:
let mut acc: Vec<Result<Item, Error>> = Vec::new();
let mut add = |v| acc.push(Ok(v));
if cond1 {
add(x1);
add(x2);
}
if cond2 {
add(y3);
}
The problem now though is that the add
closure borrows the vec. If there's even one case where you want to use the env without the helper function, you just can't:
let mut acc: Vec<Result<Item, Error>> = Vec::new();
let mut add = |v| acc.push(Ok(v));
if cond1 {
add(x1);
add(x2);
}
if unlucky {
acc.push(Err(e)); // :-(
}
if cond2 {
add(y3);
}
The problem here is that add
borrows vec
for while the add
is live, while what we actually want here is to only borrow acc
for the duration of the call itself, such that we can use it in between the call to add
.
The two common workarounds here are:
making add
a macro:
let mut acc: Vec<Result<Item, Error>> = Vec::new();
macro_rules! add { ($v:expr) => (acc.push(Ok($v))) }
adding acc as an explicit parameter to closure:
let mut acc: Vec<Result<Item, Error>> = Vec::new();
let mut add = |acc: &mut Vec<Result<Item, Error>>, v| acc.push(Ok(v));
Neither version is particularly ergonomic or nice.
Are there any hypothetical or in-progress language features which can help with this pattern?