Function
Function Item
Function items are zero-sized. Every none generic function has a unique, unnameable function item type.
fn callable() {}
let fn_item = callable; // function item: fn() {callable}
fn generic<T>() {}
let mut fn_i32 = generic::<i32>; // fn() {generic::<i32>}
// fn_i32 = generic::<u32>; // ERROR: mismatched types, fn() {generic::<u32>}
// zero-sized
println!("{}", std::mem::size_of(fn_item))
println!("{}", std::mem::size_of(fn_i32))
Function Pointer: fn
Function pointer are pointers that points to a function, referring to a function whose identity is not known at compile time.
// a function that takes two `i32` and returns one `i32`
type BinOp = fn(i32, i32) -> i32;
fn add(a: i32, b: i32) -> i32 { a + b }
fn minus(a: i32, b: i32) -> i32 { a - b }
// coerce: from "fn item" to "fn pointer"
let mut bo: BinOp = add;
bo = minus;
Closure
Closure is a function that is able to captures its environment.
// none-capturing closure
let add = |a: i32, b: i32| a + b;
// capturing closure (immutable)
let x = 10;
let read_x = |a: i32| a + x;
// capturing closure (mutable)
let mut x = 10;
let mut_x = |a: i32| { x += a; };
// capturing closure (move)
let x = 10;
let drop_x = || { drop (x); };
At compiler implementation level, closures are divided into two types:
- None-capturing: They are like unnamed function, aka. instructions written in the code.
- Capturing: They are like a struct that contains the captured variables, which also implements the corresponding function traits.
// consider a capturing closure like this
let x = 10;
let read_x = |a: i32| a + x;
read_x(5);
// it would be interpreted as something like this
let x = 10;
struct ReadX {
x: i32,
}
impl Fn(i32) -> i32 for ReadX {
fn call(&self, a: i32) -> i32 {
a + self.x
}
}
impl FnMut(i32) -> i32 for ReadX { /* ... */ }
impl FnOnce(i32) -> i32 for ReadX { /* ... */ }
let read_x = ReadX { x };
read_x.call(5);
Function Traits: Fn
, FnMut
& FnOnce
Fn
, FnMut
and FnOnce
are traits that are implemented on function types.
Fn
: The function can be called immutably, modifying nothing.FnMut
: The function can be called mutably, modifying captured data.FnOnce
: The function can be called only once, moving the captured value.
fn add(a: i32, b: i32) -> i32 { a + b }
fn call_it<F: Fn(i32, i32) -> i32>(f: F, a: i32, b: i32) -> i32 {
f(a, b)
}
call_it(add, 1, 2);
Note that Fn
is a super trait of FnMut
, and FnMut
is a super trait of
FnOnce
, because every "shared" function can be called "exclusively", and
every "exclusive" function can be called "at least once".
fn add(a: i32, b: i32) -> i32 { a + b }
fn call_once<F: FnOnce(i32, i32) -> i32>(f: F, a: i32, b: i32) -> i32 {
f(a, b)
}
fn call_mut<F: FnMut(i32, i32) -> i32>(f: F, a: i32, b: i32) -> i32 {
f(a, b)
}
call_once(add, 1, 2);
call_mut(add, 1, 2);