trait FnOnce<Args>
trait FnMut<Args>: FnOnce<Args>
trait Fn<Args>: FnMut<Args>
Note:
- Instances of FnOnce can only be called once.
- Instances of FnMut can be called repeatedly and may mutate state.
- Instances of Fn can be called repeatedly without mutating state.
Fn
(a trait) andfn
(a function pointer) are different!
- Function Pointers
- Closures
fn add_one(x: usize) -> usize {
x + 1
}
fn main() {
let ptr: fn(usize) -> usize = add_one;
println!("ptr(5) = {}", ptr(5));
}
- Defined with
|<args>|
- Most basic kind, are just function pointers
fn main() {
let clos: fn(usize) -> usize = |x| x + 5;
println!("clos(5) = {}", clos(5));
}
- Closures can capture their environment.
- Now it's an anonymous
struct
, not afn
- It implements
Fn
fn main() {
let increase_by = 1;
let clos = |x| x + increase_by;
println!("clos(5) = {}", clos(5));
}
The variable increase_by
that is captured by the closure here is called an upvar
or a free variable.
- Closures can capture their environment by mutable reference
- Now it implements
FnMut
fn main() {
let mut total = 0;
let mut update = |x| total += x;
update(5);
update(5);
println!("total: {}", total);
}
Note:
The closure is dropped before the println!
, making total
accessible again (the &mut ref stored in the closure is now gone).
If you try and call update()
after the println!
you get a compile error.
This closure implements FnOnce
.
fn main() {
let items = vec![1, 2, 3, 4];
let update = move || {
for item in items {
println!("item is {}", item);
}
};
update();
// println!("items is {:?}", items);
}
- But why is this useful?
- It makes iterators really powerful!
fn main() {
let items = [1, 2, 3, 4, 5, 6];
let n = 2;
for even_number in items.iter().filter(|x| (**x % n) == 0) {
println!("{} is even", even_number);
}
}
It's also very powerful if you have something you need to clean up.
- You do some set-up
- You want do some work (defined by the caller)
- You want to clean up after.
fn setup_teardown<F, T>(f: F) -> T where F: FnOnce(&mut Vec<u32>) -> T {
let mut state = Vec::new();
println!("> Setting up state");
let t = f(&mut state);
println!("< State contains {:?}", state);
t
}
fn setup_teardown<F, T>(f: F) -> T where F: FnOnce(&mut Vec<u32>) -> T {
let mut state = Vec::new();
println!("> Setting up state");
let t = f(&mut state);
println!("< State contains {:?}", state);
t
}
fn main() {
setup_teardown(|s| s.push(1));
setup_teardown(|s| {
s.push(1);
s.push(2);
s.push(3);
});
}
Note:
In release mode, all this code just gets inlined.