I sometimes use little helper closures which perform certain tasks on local variables for me. Consider the following simple example where I have a sum
that is initialized with 0
and a closure add
, which allows me to add values to it. Let's assume I also have a second closure double
, which will double the stored sum.
The naive approach doesn't work:
fn main() {
let mut sum = 0;
let mut add = |addend| sum += addend;
//let mut double = || sum *= 2;
add(4);
add(5);
//double();
add(1);
//double();
println!("{}", sum);
}
Because if I uncomment the lines involving doubling, sum
is both borrowed mutably by the add
and by the double
closure. (You can try by uncommenting the lines above.)
There are several solutions:
- using
RefCell
, - passing a mutable reference to
sum
when calling each closure, - avoid having two closures doing the operation by using an
enum
to branch.
These three approaches would look as follows:
use std::cell::RefCell;
fn using_cell() {
let sum = RefCell::new(0);
let add = |addend| *sum.borrow_mut() += addend;
let double = || *sum.borrow_mut() *= 2;
add(4);
add(5);
double();
add(1);
double();
let sum = sum.into_inner();
println!("{}", sum);
}
fn using_mut() {
let mut sum = 0;
let add = |sum: &mut _, addend| *sum += addend;
let double = |sum: &mut _| *sum *= 2;
add(&mut sum, 4);
add(&mut sum, 5);
double(&mut sum);
add(&mut sum, 1);
double(&mut sum);
println!("{}", sum);
}
fn using_enum() {
let mut sum = 0;
enum Action { Add(i32), Double() }
use Action::*;
let mut doit = |act: Action| match act {
Add(addend) => sum += addend,
Double() => sum *= 2,
};
doit(Add(4));
doit(Add(5));
doit(Double());
doit(Add(1));
doit(Double());
println!("{}", sum);
}
fn main() {
using_cell();
using_mut();
using_enum();
}
Output:
38
38
38
I wonder, which of the three ways do you think is most idiomatic?