Binding Indirectly accessing a variable makes it impossible to branch and use that variable without re-binding. match provides the @ sigil for binding values to names: // A function `age` which returns a `u32`. fn age() -> u32 { 15 } fn mai...
if let For some use cases, when matching enums, match is awkward. For example: // Make `optional` of type `Option<i32>` let optional = Some(7); match optional { Some(i) => println!("This is a really long string and `{:?}`", i), _ => {}, //...
let-else 🛈 stable since: rust 1.65 🛈 you can target specific edition by compiling like this rustc --edition=2021 main.rs With let-else, a refutable pattern can match and bind variables in the surrounding scope like a normal let, or else div...
while let Similar to if let, while let can make awkward match sequences more tolerable. Consider the following sequence that increments i: // Make `optional` of type `Option<i32>` let mut optional = Some(0); // Repeatedly try this test. loo...
Functions Functions are declared using the fn keyword. Its arguments are type annotated, just like variables, and, if the function returns a value, the return type must be specified after an arrow ->. The final expression in the function wi...
Associated functions & Methods Some functions are connected to a particular type. These come in two forms: associated functions, and methods. Associated functions are functions that are defined on a type generally, while methods are associa...
Closures Closures are functions that can capture the enclosing environment. For example, a closure that captures the x variable: |val| val + x The syntax and capabilities of closures make them very convenient for on the fly usage. Calling a...
Capturing Closures are inherently flexible and will do what the functionality requires to make the closure work without annotation. This allows capturing to flexibly adapt to the use case, sometimes moving and sometimes borrowing. Closures...
As input parameters While Rust chooses how to capture variables on the fly mostly without type annotation, this ambiguity is not allowed when writing functions. When taking a closure as an input parameter, the closure's complete type must b...
Type anonymity Closures succinctly capture variables from enclosing scopes. Does this have any consequences? It surely does. Observe how using a closure as a function parameter requires generics, which is necessary because of how they are d...
Input functions Since closures may be used as arguments, you might wonder if the same can be said about functions. And indeed they can! If you declare a function that takes a closure as parameter, then any function that satisfies the trait...
As output parameters Closures as input parameters are possible, so returning closures as output parameters should also be possible. However, anonymous closure types are, by definition, unknown, so we have to use impl Trait to return them. T...
Examples in std This section contains a few examples of using closures from the std library.
Iterator::any Iterator::any is a function which when passed an iterator, will return true if any element satisfies the predicate. Otherwise false. Its signature: pub trait Iterator { // The type being iterated over. type Item; // `any` take...
Searching through iterators Iterator::find is a function which iterates over an iterator and searches for the first value which satisfies some condition. If none of the values satisfy the condition, it returns None. Its signature: pub trait...
Higher Order Functions Rust provides Higher Order Functions (HOF). These are functions that take one or more functions and/or produce a more useful function. HOFs and lazy iterators give Rust its functional flavor. fn is_odd(n: u32) -> bool...
Diverging functions Diverging functions never return. They are marked using !, which is an empty type. fn foo() -> ! { panic!("This call never returns."); } As opposed to all the other types, this one cannot be instantiated, because the set...
Modules Rust provides a powerful module system that can be used to hierarchically split code in logical units (modules), and manage visibility (public/private) between them. A module is a collection of items: functions, structs, traits, imp...
Visibility By default, the items in a module have private visibility, but this can be overridden with the pub modifier. Only the public items of a module can be accessed from outside the module scope. // A module named `my_mod` mod my_mod {...
Struct visibility Structs have an extra level of visibility with their fields. The visibility defaults to private, and can be overridden with the pub modifier. This visibility only matters when a struct is accessed from outside the module w...