Rust's Closure
@shulhi
What's a closure?
A function that can directly use variables from the scope in which it is defined
fn main() {
let text = String::from("Hello");
let my_closure = || {
// text is called _captured_ variable
do_something(&text)
};
my_closure();
}
fn add_one(x: i32) -> impl Fn() -> i32 {
let one = 1;
|| x + one
}
fn main() {
let five = 5;
let calculate = add_one(five);
println!("{}", calculate());
}
function add_one(x) {
var one = 1;
return function() {
one + x;
}
}
function main() {
var five = 5;
var closure = add_one(five);
var result = closure();
print(result);
...
}
Language with GC
Rust (No GC)
demo: step-1
function add_one(x) {
var one = 1;
return one + x;
}
function main() {
var five = 5;
var result = add_one(five);
print(result);
...
}
add_one()
one: 1
result: 6
one
main()
result
five
five: 5
Stack Frames
Heap
function add_one(x) {
var one = 1;
return function() {
return one + x;
}
}
function main() {
var five = 5;
var calculate = add_one(five);
print(calculate());
...
something();
}
add_one()
one: 1
calculate: fn
one
main()
calculate
five
five: 5
Stack Frames
Heap
add_one()
calculate: fn
one
main()
calculate
five
five: 5
Stack Frames
*Heap
Rust's solution: Borrow (default) or Move
one: 1
*depends where you initialize the data
one: 1
calculate: fn
main()
calculate
five
five: 5
Stack Frames
*Heap
Rust's solution: Move & Borrow
*depends where you initialize the data
Closure's Implementation
How it really works?
Fn: &self
FnOnce: self
FnMut: &mut self
Traits
How Rust determines the trait to implement?
Consume its captured variables?
Yes
No
Doesn't mutate its captured variables?
Fn
FnMut
FnOnce
FnOnce
How Rust determines the trait to implement?
let s = String::from("hello world");
let my_fnonce = move || { s };
let s = "hello world";
let my_fn = move || { s.length() }
The move modifier controls how captures are moved into the closure when it's created. FnMut membership is determined by how captures are moved out of the closure (or consumed in some other way) when it's executed.
fn main() {
let some_num = 5;
let some_closure = || {
let x = some_num;
x + 1;
};
}
fn main::{{closure}}(_1: &[closure@src/main.rs:4:24: 7:6 some_num:&i32]) -> (){
let mut _0: (); // return place
scope 1 {
}
scope 2 {
let _2: i32; // "x" in scope 2 at src/main.rs:5:13: 5:14
}
let mut _3: i32;
let mut _4: i32;
let mut _5: (i32, bool);
bb0: {
StorageLive(_2); // bb0[0]: scope 0 at src/main.rs:5:13: 5:14
_2 = (*((*_1).0: &i32)); // bb0[1]: scope 0 at src/main.rs:5:17: 5:25
StorageLive(_4); // bb0[2]: scope 1 at src/main.rs:6:9: 6:10
_4 = _2; // bb0[3]: scope 1 at src/main.rs:6:9: 6:10
_5 = CheckedAdd(move _4, const 1i32); // bb0[4]: scope 1 at src/main.rs:6:9: 6:14
}
MIR output
fn main() {
let some_num: i32 = 5;
let some_closure = || {
let x: ??? = some_num;
x + 1;
};
}
What is the type of x?
&i32 or i32
fn main() {
let some_num = 5;
let some_closure = move || {
let x = some_num;
x + 1;
};
}
fn main::{{closure}}(_1: &[closure@src/main.rs:4:24: 7:6 some_num:i32]) -> (){
let mut _0: (); // return place
scope 1 {
}
scope 2 {
let _2: i32; // "x" in scope 2 at src/main.rs:5:13: 5:14
}
let mut _3: i32;
let mut _4: i32;
let mut _5: (i32, bool);
bb0: {
StorageLive(_2); // bb0[0]: scope 0 at src/main.rs:5:13: 5:14
_2 = ((*_1).0: i32); // bb0[1]: scope 0 at src/main.rs:5:17: 5:25
StorageLive(_4); // bb0[2]: scope 1 at src/main.rs:6:9: 6:10
_4 = _2; // bb0[3]: scope 1 at src/main.rs:6:9: 6:10
_5 = CheckedAdd(move _4, const 1i32); // bb0[4]: scope 1 at src/main.rs:6:9: 6:14
}
MIR output
Closure in Rust
By Shulhi Sapli
Closure in Rust
- 1,023