Functions
Statements and Expressions
// Statements end in ;
// Do not return value, or return ()
let x = 5;
x;
// Expressions do not end in ;
// Return value
// {} blocks are expressions
let z = {
2
}
Functions
- Associated function is within
impl
- Method is an associated fn that takes
self or &self
- Ways to call a method:
x.fn() call as a method
X::fn(x), X::fn(&x) or X::fn(&mut x) call as full method syntax
S::fn(&x) if X derefs to S, i.e. x.fn() finds methods of S
T::fn(&x) if X impl T, i.e. x.fn() finds methods of T which needs to be in scope
- Call associated function (does not take
self)
X::fn(), e.g. X::new()
<X as T>::fn() call trait method T::fn() for X
- For methods with generic definition, use turbofish syntax if compiler cant infer
X::<type>::fn(), e.g. Vec::<String>::new()
x.fn::<type>(), e.g. "4".parse::<u32>()
// snake_case
// no restriction on order of fn definitions
fn name(x: i32, y: i64) -> bool {
statements;
expression
}
// when no return specified, it's returning ()
fn name() { ... }
// `!` is actual no return allowed
fn last_fn() -> ! { ... }
// statement: perform action, do not reutn a value
// expression: evaluate to result a value, no ; at end
{
variable
} // variable will drop after {}
// calling a function/macro is an expression
let y = {
let x = 3;
x + 1 // remember no ;
};
// return value
// return with expression
fn five() -> i32 {
5
}
// associated functions: defined on a type
SomeStruct::init(x: 0.0, y: 0.0);
// methods: called on an instance of a type
rectu.area();
// function call or method call with genetic type
// turbofish, need ::<...> between method name and ()
Vec::<i32>::with_capacity(1000) // need ::< or drop the ::<i32> and let compiler infer
let ramp = (0 .. n).collect::<Vec<i32>>();
let ramp: Vec<i32> = (0 .. n).collect(); // reads better
let four = "4".parse::<u32>();
Closure
- Anonymous: no function name needed
let closure = |val: i32| -> i32 { val + 1};
let closreu = || 1; // return 1
let clos = |num| {
println!("doing something");
// more code
};
closure(); // similar way to call closure
// |_|
some.map(|_| println!("we need to take para, but we dont care"));
fn() -> SomeType // this is a function only
Fn() -> SomeType // this is Fn trait, function and closures
// Redundant_closure
// if `foo(_)` is a plain fn that takes the exact arg type of x
//bad
xs.map(|x| foo(x));
// good
xs.map(foo);
- Capture env: use variable ourside closure
Fn: borrows value from env immutably; subtrait of FnMut
FnMut: mutably borrows value, subtrait of FnOnce
FnOnce: take ownership and move into the closure; only call once
move: force move to closure
Function Pointers
fn(T) -> U is a function pointer, point to code
- all safe function pointers impl
Fn, FnMut and FnOnce
- In comparison,
Fn* is a trait
// passing function as a parameter
// using trait
fn map<T, U, F>(input: Vec<U>, mut function: F) -> Vec<T>
where F: FnMut(U) -> T,
{
let mut output: Vec<T> = vec![];
for t in input {
output.push(function(t));
}
output
}
// passing fn as a parameter
// dont have to deal with lifetime
// but cannot accept closure that capture env
fn matcher<T, S>(matcher: fn(T) -> bool, sub: S) -> Matcher<T> {}
// function-type field in struct
struct Matcher<T> {
func: fn(T) -> bool,
sub: String,
}
// to call a function-type field
let s = Matcher::<T>::new();
(s.func)(some_t)