Hello everyone, new episode of rust series is here, today we will discuss closures in Rust. Closures, also called lambdas, are functions that can capture the enclosing environment.
fn main() {
let power = | x | x * x;
let res = power(2);
println!("Result: {}", res);
}
# output
Result: 4
This example above is pretty straight and without any issues.
let mut addition = 2;
let plus_two = | x | x + addition;
let res2 = plus_two(10);
addition = 3;
# output
error: cannot assign to `addition` because it is borrowed
Changing value of addition cannot be done as addition is already borrowed by closure
let nums = vec![1, 2, 3];
let takes_nums = || nums;
println!("{:?}", nums);
# output
error: use of moved value: `nums`
This cannot be done as closure must take ownership of vector content here. Note that this is not an issue with array or other read-only structures or those that can by simply copied.
Rust provides keyword move so closure can take ownership of its environment. Sound strange but basically it creates copy of a value used inside closure.
let mut num = 10;
let mut add_num = move |x: i32| {
num += x;
println!("num value in closure: {}", num);
};
println!("num value before calling closure: {}", num);
add_num(10);
println!("num value after calling closure: {}", num);
# output
num value before calling closure: 10
num value in closure: 20
num value after calling closure: 10
Now check this code below that is using the original variable. Note that variable scope is slightly updated not to interfere with ownership protection.
let mut num = 10;
println!("num value before calling closure: {}", num);
{
let mut add_num = |x: i32| {
num += x;
println!("num value in closure: {}", num);
};
add_num(10);
}
println!("num value after calling closure: {}", num);
# output
num value before calling closure: 10
num value in closure: 20
num value after calling closure: 20
Now we truly work with original num variable, not a copy.
Closure can be used as an regular function argument.
fn main() {
fn call_with_ten<F>(some_closure: F) -> i32
where F: Fn(i32) -> i32 {
some_closure(10)
}
let powered_result = call_with_ten(|x| x * x);
let half_result = call_with_ten(|x| x / 2);
println!("pow result: {}", powered_result);
println!("half result: {}", half_result);
}
# output
pow result: 100
half result: 5
We've defined function that will call various closures with argument 10.
We can also use functions as closure input for other function, check code below providing the same functionality as last example.
fn main() {
fn call_with_ten(some_closure: &Fn(i32) -> i32) -> i32 {
some_closure(10)
}
fn powered_function(i: i32) -> i32{
i * i
}
fn half_function(i: i32) -> i32 {
i / 2
}
let powered_result = call_with_ten(&powered_function);
let half_result = call_with_ten(&half_function);
println!("pow result: {}", powered_result);
println!("half result: {}", half_result);
}
# output
pow result: 100
half result: 5
Closure factory is some function that returns closure. It's quite common pattern. There are some principle that must be used to cross some limitations
After applying these principles we can create factory like this
fn main() {
fn factory_function() -> Box<Fn(i32) -> i32> {
let num = 10;
Box::new(move |x| x + num)
}
let produced_function = factory_function();
let result = produced_function(10);
println!("result: {}", result );
}
# output
result: 20
That's all for today, thank you for your appreciations, feel free to comment and point out possible mistakes (first 24 hours works the best but any time is fine). May Jesus bless your programming skills, use them wisely and see you next time.
Meanwhile you can also check the official documentation for closures and if you wish.