Inline function definitions which can be bound to variables. The function block is executed when the closure is called.
let square = |x: i32| -> i32 { x * x };println!("{}", square(3));// => 6
Inline function definitions which can be bound to variables. The function block is executed when the closure is called.
let foo_v1 = |x: i32| { x * x };let foo_v2 = |x: i32, y: i32| x * y;let foo_v3 = |x: i32| { // Very Important Arithmetic let y = x * 2; let z = 4 + y; x + y + z};let foo_v4 = |x: i32| if x == 0 { 0 } else { 1 };
||
, followed by the return expression.{}
.let
instead of fn
let square_v4 = |x: u32| { (x * x) as i32 };let square_v4 = |x| -> i32 { x * x }; // ← unable to infer enoughlet square_v4 = |x| { x * x }; // ← type information!
x
from
the return expression x * x
.Having concrete function types for type inference and self-documentation. For closures, ease of use is more important.
let magic_num = 5;let magic_johnson = 32;let plus_magic = |x: i32| x + magic_num;
plus_magic
is able to reference magic_num
even though it's not
passed as an argument.magic_num
is in the "environment" of the closure.magic_johnson
is not borrowed!magic_num
in a conflicting way after the
closure is bound, we'll get an error from the compiler:let mut magic_num = 5;let magic_johnson = 32;let plus_magic = |x: i32| x + magic_num;let more_magic = &mut magic_num; // Err!println!("{}", magic_johnson); // Ok!
error: cannot borrow `magic_num` as mutable because it is already borrowed as immutable [...] the immutable borrow prevents subsequent moves or mutable borrows of `magic_num` until the borrow ends
plus_magic
borrows magic_num
when it closes over it!magic_johnson
is not used in the closure, and its ownership is not
affected.let mut magic_num = 5;{ let plus_magic = |x: i32| x + magic_num;} // the borrow of magic_num ends herelet more_magic = &mut magic_num; // Ok!println!("magic_num: {}", more_magic);
Questions?
move
keyword.let mut magic_num = 5;let own_the_magic = move |x: i32| x + magic_num;let more_magic = &mut magic_num;
move
closures are necessary when the closure f
needs to outlive the scope in
which it was created.f
into a thread, or return f
from a function.move
essentially disallows bringing references into the closure.fn make_closure(x: i32) -> Box<Fn(i32) -> i32> { let f = move |y| x + y; // ^ more on this in 15 seconds Box::new(f)}let f = make_closure(2);println!("{}", f(3));
Sometimes, a closure must take ownership of an environment variable to be
valid. This happens automatically (without move
):
If the value is moved into the return value.
let lottery_numbers = vec![11, 39, 51, 57, 75];{ let ticket = || { lottery_numbers };}// The braces do no good here.println!("{:?}", lottery_numbers); // use of moved value
Or moved anywhere else.
let numbers = vec![2, 5, 32768];let alphabet_soup = || { numbers; vec!['a', 'b'] }; // ^ throw away unneeded ingredientsprintln!("{:?}", numbers); // use of moved value
If the type is not Copy
, the original variable is invalidated.
let numbers = vec![2, 5, 32768];let alphabet_soup = || { numbers; vec!['a', 'b'] }; // ^ throw away unneeded ingredientsalphabet_soup();alphabet_soup(); // use of moved value
move
behavior is implicit because alphabet_soup
must own numbers
to
move it.let numbers = vec![2, 5, 32768];let alphabet_soup = move || { println!("{:?}", numbers) };alphabet_soup();alphabet_soup(); // Delicious soup
Fn
, FnMut
, FnOnce
- method calls are overloadable operators.pub trait Fn<Args> : FnMut<Args> { extern "rust-call" fn call(&self, args: Args) -> Self::Output;}pub trait FnMut<Args> : FnOnce<Args> { extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;}pub trait FnOnce<Args> { type Output; extern "rust-call" fn call_once(self, args: Args) -> Self::Output;}
self
:Fn
borrows self
as &self
FnMut
borrows self
mutably as &mut self
FnOnce
takes ownership of self
Fn
is a superset of FnMut
, which is a superset of FnOnce
."The || {}
syntax for closures is sugar for these three traits. Rust will
generate a struct for the environment, impl the appropriate trait, and then use
it."¹
¹Taken from the Rust Book
map
¹.// self = Vec<A>fn map<A, B, F>(self, f: F) -> Vec<B> where F: FnMut(A) -> B;
map
takes an argument f: F
, where F
is an FnMut
trait object.¹Real map
coming in next lecture.
fn i_need_some_closure() -> (Fn(i32) -> i32) { let local = 2; |x| x * local}
error: the trait `core::marker::Sized` is not implemented for the type `core::ops::Fn(i32) -> i32 + 'static`
Fn
object is not of constant size at compile time.Fn
.Fn
in a layer of indirection and return a reference!fn i_need_some_closure_by_reference() -> &(Fn(i32) -> i32) { let local = 2; |x| x * local}
error: missing lifetime specifier
Box
!fn box_me_up_that_closure() -> Box<Fn(i32) -> i32> { let local = 2; Box::new(|x| x * local)}
error: closure may outlive the current function, but itborrows `local`, which is owned by the current function [E0373]
box_me_up_that_closure
returns, local
will be destroyed.fn box_up_your_closure_and_move_out() -> Box<Fn(i32) -> i32> { let local = 2; Box::new(move |x| x * local)}
let square = |x: i32| -> i32 { x * x };println!("{}", square(3));// => 6
Inline function definitions which can be bound to variables. The function block is executed when the closure is called.
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |