Files
my-vault/00_Inbox/Clippings/2023/04/A half-hour to learn Rust.md
T

1926 lines
46 KiB
Markdown
Raw Normal View History

2026-01-05 13:03:55 +08:00
---
page-title: "A half-hour to learn Rust"
url: https://fasterthanli.me/articles/a-half-hour-to-learn-rust
date: "2023-04-02 09:39:44"
---
Jan 27, 2020 20 minute read [rust](https://fasterthanli.me/tags/rust)
In order to increase fluency in a programming language, one has to read a lot of it. But how can you read a lot of it if you don't know what it means?
In this article, instead of focusing on one or two concepts, I'll try to go through as many Rust snippets as I can, and explain what the keywords and symbols they contain mean.
Ready? Go!
`let` introduces a variable binding:
```rust
let x;// declare "x"
x = 42;// assign 42 to "x"
```
This can also be written as a single line:
```rust
let x = 42;
```
You can specify the variable's type explicitly with `:`, that's a type annotation:
```rust
let x: i32; // `i32` is a signed 32-bit integer
x = 42;
// there's i8, i16, i32, i64, i128
// also u8, u16, u32, u64, u128 for unsigned
```
This can also be written as a single line:
```rust
let x: i32 = 42;
```
If you declare a name and initialize it later, the compiler will prevent you from using it before it's initialized.
```rust
let x;
foobar(x);// error: borrow of possibly-uninitialized variable: `x`
x = 42;
```
However, doing this is completely fine:
```rust
let x;
x = 42;
foobar(x);// the type of `x` will be inferred from here
```
The underscore `_` is a special name - or rather, a "lack of name". It basically means to throw away something:
```rust
// this does *nothing* because 42 is a constant
let _ = 42;
// this calls `get_thing` but throws away its result
let _ = get_thing();
```
Names that *start* with an underscore are regular names, it's just that the compiler won't warn about them being unused:
```rust
// we may use `_x` eventually, but our code is a work-in-progress
// and we just wanted to get rid of a compiler warning for now.
let _x = 42;
```
Separate bindings with the same name can be introduced - you can *shadow* a variable binding:
```rust
let x = 13;
let x = x + 3;
// using `x` after that line only refers to the second `x`,
// the first `x` no longer exists.
```
Rust has tuples, which you can think of as "fixed-length collections of values of different types".
```rust
let pair = ('a',17);
pair.0;// this is 'a'
pair.1;// this is 17
```
If we really wanted to annotate the type of `pair`, we would write:
```rust
let pair: (char, i32) = ('a', 17);
```
Tuples can be destructured when doing an assignment, which means they're broken down into their individual fields:
```rust
let (some_char, some_int) = ('a', 17);
// now, `some_char` is 'a', and `some_int` is 17
```
This is especially useful when a function returns a tuple:
```Rust
let (left, right) = slice.split_at(middle);
```
Of couse, when destructuring a tuple, `_` can be used to throw away part of it:
```rust
let (_, right) = slice.split_at(middle);
```
The semi-colon marks the end of a statement:
```rust
let x = 3;
let y = 5;
let z = y + x;
```
Which means statements can span multiple lines:
```Rust
let x = vec![1, 2, 3, 4, 5, 6, 7, 8]
.iter()
.map(|x| x + 3)
.fold(0, |x, y| x + y);
```
(We'll go over what those actually mean later).
`fn` declares a function.
Here's a void function:
```Rust
fn greet(){
println!("Hi there!");
}
```
And here's a function that returns a 32-bit signed integer. The arrow indicates its return type:
```Rust
fn fair_dice_roll() -> i32 {
4
}
```
A pair of brackets declares a block, which has its own scope:
```Rust
// This prints "in", then "out"*
fn main() {
let x = "out";
{
// this is a different `x`
let x = "in";
println!("{}", x);
}
println!("{}", x);
}
```
Blocks are also expressions, which mean they evaluate to.. a value.
```Rust
// this:
let x = 42;
// is equivalent to this:
let x = { 42 };
```
Inside a block, there can be multiple statements:
```Rust
let x = {
let y = 1;// first statement
let z = 2;// second statement
y + z // this is the 'tail' - what the whole block will evaluate to
};
```
And that's why "omitting the semicolon at the end of a function" is the same as returning, ie. these are equivalent:
```Rust
fn fair_dice_roll() -> i32 {
return 4;
}
fn fair_dice_roll() -> i32 {
4
}
```
`if` conditionals are also expressions:
```Rust
fn fair_dice_roll() -> i32 {
if feeling_lucky {
6
} else {
4
}
}
```
A `match` is also an expression:
```Rust
fn fair_dice_roll() -> i32 {
match feeling_lucky {
true => 6,
false => 4,
}
}
```
Dots are typically used to access fields of a value:
```Rust
let a = (10, 20);
a.0; // this is 10
let amos = get_some_struct();
amos.nickname; // this is "fasterthanlime"
```
Or call a method on a value:
```Rust
let nick = "fasterthanlime";
nick.len(); // this is 14
```
The double-colon, `::`, is similar but it operates on namespaces.
In this example, `std` is a *crate* (~ a library), `cmp` is a *module* (~ a source file), and `min` is a *function*:
```rust
let least = std::cmp::min(3, 8); // this is 3
```
`use` directives can be used to "bring in scope" names from other namespace:
```Rust
use std::cmp::min;
let least = min(7, 1);// this is 1
```
Within `use` directives, curly brackets have another meaning: they're "globs". If we want to import both `min` and `max`, we can do any of these:
```rust
// this works:
use std::cmp::min;
use std::cmp::max;
// this also works:
use std::cmp::{min, max};
// this also works!
use std::{cmp::min, cmp::max};
```
A wildcard (`*`) lets you import every symbol from a namespace:
```Rust
// this brings `min` and `max` in scope, and many other things
use std::cmp::*;
```
Types are namespaces too, and methods can be called as regular functions:
```Rust
let x = "amos".len(); // this is 4
let x = str::len("amos"); // this is also 4
```
`str` is a primitive type, but many non-primitive types are also in scope by default.
```Rust
// `Vec` is a regular struct, not a primitive type*
let v = Vec::new();
// this is exactly the same code, but with the *full* path to `Vec`
let v = std::vec::Vec::new();
```
This works because Rust inserts this at the beginning of every module:
```Rust
use std::prelude::v1::*;
```
(Which in turns re-exports a lot of symbols, like `Vec`, `String`, `Option` and `Result`).
Structs are declared with the `struct` keyword:
```Rust
struct Vec2 {
x: f64, // 64-bit floating point, aka "double precision"
y: f64,
}
```
They can be initialized using *struct literals*:
```Rust
let v1 = Vec2 { x: 1.0, y: 3.0 };
let v2 = Vec2 { y: 2.0, x: 4.0 };
// the order does not matter, only the names do
```
There is a shortcut for initializing the *rest of the fields* from another struct:
Rust code
*let* v3 = *Vec2* *{*
*x**:* *14.0**,*
..v2
*}**;*
This is called "struct update syntax", can only happen in last position, and cannot be followed by a comma.
Note that *the rest of the fields* can mean *all the fields*:
Rust code
*let* v4 = *Vec2* *{* ..v3 *}**;*
Structs, like tuples, can be destructured.
Just like this is a valid `let` pattern:
Rust code
*let* *(*left*,* right*)* = slice*.**split\_at**(*middle*)**;*
So is this:
Rust code
*let* v = *Vec2* *{* *x**:* *3.0**,* *y**:* *6.0* *}**;*
*let* *Vec2* *{* x*,* y *}* = v*;*
*// \`x\` is now 3.0, \`y\` is now \`6.0\`*
And this:
Rust code
*let* *Vec2* *{* x*,* .. *}* = v*;*
*// this throws away \`v.y\`*
`let` patterns can be used as conditions in `if`:
Rust code
*struct* *Number* *{*
*odd**:* *bool**,*
*value**:* *i32**,*
*}*
*fn* *main**(**)* *{*
*let* one = *Number* *{* *odd**:* *true**,* *value**:* *1* *}**;*
*let* two = *Number* *{* *odd**:* *false**,* *value**:* *2* *}**;*
*print\_number**(*one*)**;*
*print\_number**(*two*)**;*
*}*
*fn* *print\_number**(**n**:* *Number**)* *{*
*if* *let* *Number* *{* *odd**:* *true**,* value *}* = n *{*
*println**!**(**"Odd number: {}"*, value*)**;*
*}* *else* *if* *let* *Number* *{* *odd**:* *false**,* value *}* = n *{*
*println**!**(**"Even number: {}"*, value*)**;*
*}*
*}*
*// this prints:*
*// Odd number: 1*
*// Even number: 2*
`match` arms are also patterns, just like `if let`:
Rust code
*fn* *print\_number**(**n**:* *Number**)* *{*
*match* n *{*
*Number* *{* *odd**:* *true**,* value *}* => *println**!**(**"Odd number: {}"*, value*)**,*
*Number* *{* *odd**:* *false**,* value *}* => *println**!**(**"Even number: {}"*, value*)**,*
*}*
*}*
*// this prints the same as before*
A `match` has to be exhaustive: at least one arm needs to match.
Rust code
*fn* *print\_number**(**n**:* *Number**)* *{*
*match* n *{*
*Number* *{* *value**:* *1**,* .. *}* => *println**!**(**"One"**)**,*
*Number* *{* *value**:* *2**,* .. *}* => *println**!**(**"Two"**)**,*
*Number* *{* value*,* .. *}* => *println**!**(**"{}"*, value*)**,*
*// if that last arm didn't exist, we would get a compile-time error*
*}*
*}*
If that's hard, `_` can be used as a "catch-all" pattern:
Rust code
*fn* *print\_number**(**n**:* *Number**)* *{*
*match* n*.**value* *{*
*1* => *println**!**(**"One"**)**,*
*2* => *println**!**(**"Two"**)**,*
\_ => *println**!**(**"{}"*, n.value*)**,*
*}*
*}*
You can declare methods on your own types:
Rust code
*struct* *Number* *{*
*odd**:* *bool**,*
*value**:* *i32**,*
*}*
*impl* *Number* *{*
*fn* *is\_strictly\_positive**(**self**)* -> *bool* *{*
*self**.**value* > *0*
*}*
*}*
And use them like usual:
Rust code
*fn* *main**(**)* *{*
*let* minus\_two = *Number* *{*
*odd**:* *false**,*
*value**:* -*2**,*
*}**;*
*println**!**(**"positive? {}"*, minus\_two.is\_strictly\_positive*(**)**)**;*
*// this prints "positive? false"*
*}*
Variable bindings are immutable by default, which means their interior can't be mutated:
Rust code
*fn* *main**(**)* *{*
*let* n = *Number* *{*
*odd**:* *true**,*
*value**:* *17**,*
*}**;*
n*.**odd* = *false**;* *// error: cannot assign to \`n.odd\`,*
*// as \`n\` is not declared to be mutable*
*}*
And also that they cannot be assigned to:
Rust code
*fn* *main**(**)* *{*
*let* n = *Number* *{*
*odd**:* *true**,*
*value**:* *17**,*
*}**;*
n = *Number* *{*
*odd**:* *false**,*
*value**:* *22**,*
*}**;* *// error: cannot assign twice to immutable variable \`n\`*
*}*
`mut` makes a variable binding mutable:
Rust code
*fn* *main**(**)* *{*
*let* *mut* n = *Number* *{*
*odd**:* *true**,*
*value**:* *17**,*
*}*
n*.**value* = *19**;* *// all good*
*}*
Traits are something multiple types can have in common:
Rust code
*trait* *Signed* *{*
*fn* *is\_strictly\_negative**(**self**)* -> *bool**;*
*}*
You can implement:
- one of your traits on anyone's type
- anyone's trait on one of your types
- but not a foreign trait on a foreign type
These are called the "orphan rules".
Here's an implementation of our trait on our type:
Rust code
*impl* *Signed* *for* *Number* *{*
*fn* *is\_strictly\_negative**(**self**)* -> *bool* *{*
*self**.**value* < *0*
*}*
*}*
*fn* *main**(**)* *{*
*let* n = *Number* *{* *odd**:* *false**,* *value**:* -*44* *}**;*
*println**!**(**"{}"*, n.is\_strictly\_negative*(**)**)**;* *// prints "true"*
*}*
Our trait on a foreign type (a primitive type, even):
Rust code
*impl* *Signed* *for* *i32* *{*
*fn* *is\_strictly\_negative**(**self**)* -> *bool* *{*
*self* < *0*
*}*
*}*
*fn* *main**(**)* *{*
*let* n*:* *i32* = -*44**;*
*println**!**(**"{}"*, n.is\_strictly\_negative*(**)**)**;* *// prints "true"*
*}*
A foreign trait on our type:
Rust code
*// the \`Neg\` trait is used to overload \`-\`, the*
*// unary minus operator.*
*impl* std*::*ops*::**Neg* *for* *Number* *{*
*type* *Output* = *Number**;*
*fn* *neg**(**self**)* -> *Number* *{*
*Number* *{*
*value**:* -*self**.**value**,*
*odd**:* *self**.**odd**,*
*}*
*}*
*}*
*fn* *main**(**)* *{*
*let* n = *Number* *{* *odd**:* *true**,* *value**:* *987* *}**;*
*let* m = -n*;* *// this is only possible because we implemented \`Neg\`*
*println**!**(**"{}"*, m.value*)**;* *// prints "-987"*
*}*
An `impl` block is always *for* a type, so, inside that block, `Self` means that type:
Rust code
*impl* std*::*ops*::**Neg* *for* *Number* *{*
*type* *Output* = *Self**;*
*fn* *neg**(**self**)* -> *Self* *{*
*Self* *{*
*value**:* -*self**.**value**,*
*odd**:* *self**.**odd**,*
*}*
*}*
*}*
Some traits are *markers* - they don't say that a type implements some methods, they say that certain things can be done with a type.
For example, `i32` implements trait `Copy` (in short, `i32` *is* `Copy`), so this works:
Rust code
*fn* *main**(**)* *{*
*let* a*:* *i32* = *15**;*
*let* b = a*;* *// \`a\` is copied*
*let* c = a*;* *// \`a\` is copied again*
*}*
And this also works:
Rust code
*fn* *print\_i32**(**x**:* *i32**)* *{*
*println**!**(**"x = {}"*, x*)**;*
*}*
*fn* *main**(**)* *{*
*let* a*:* *i32* = *15**;*
*print\_i32**(*a*)**;* *// \`a\` is copied*
*print\_i32**(*a*)**;* *// \`a\` is copied again*
*}*
But the `Number` struct is not `Copy`, so this doesn't work:
Rust code
*fn* *main**(**)* *{*
*let* n = *Number* *{* *odd**:* *true**,* *value**:* *51* *}**;*
*let* m = n*;* *// \`n\` is moved into \`m\`*
*let* o = n*;* *// error: use of moved value: \`n\`*
*}*
And neither does this:
Rust code
*fn* *print\_number**(**n**:* *Number**)* *{*
*println**!**(**"{} number {}"*, *if* n.odd *{* *"odd"* *}* else *{* *"even"* *}*, n.value*)**;*
*}*
*fn* *main**(**)* *{*
*let* n = *Number* *{* *odd**:* *true**,* *value**:* *51* *}**;*
*print\_number**(*n*)**;* *// \`n\` is moved*
*print\_number**(*n*)**;* *// error: use of moved value: \`n\`*
*}*
But it works if `print_number` takes an immutable reference instead:
Rust code
*fn* *print\_number**(**n**:* *&**Number**)* *{*
*println**!**(**"{} number {}"*, *if* n.odd *{* *"odd"* *}* else *{* *"even"* *}*, n.value*)**;*
*}*
*fn* *main**(**)* *{*
*let* n = *Number* *{* *odd**:* *true**,* *value**:* *51* *}**;*
*print\_number**(**&*n*)**;* *// \`n\` is borrowed for the time of the call*
*print\_number**(**&*n*)**;* *// \`n\` is borrowed again*
*}*
It also works if a function takes a *mutable* reference - but only if our variable binding is also `mut`.
Rust code
*fn* *invert**(**n**:* *&**mut* *Number**)* *{*
n*.**value* = -n*.**value**;*
*}*
*fn* *print\_number**(**n**:* *&**Number**)* *{*
*println**!**(**"{} number {}"*, *if* n.odd *{* *"odd"* *}* else *{* *"even"* *}*, n.value*)**;*
*}*
*fn* *main**(**)* *{*
*// this time, \`n\` is mutable*
*let* *mut* n = *Number* *{* *odd**:* *true**,* *value**:* *51* *}**;*
*print\_number**(**&*n*)**;*
*invert**(**&**mut* n*)**;* *// \`n is borrowed mutably - everything is explicit*
*print\_number**(**&*n*)**;*
*}*
Trait methods can also take `self` by reference or mutable reference:
Rust code
*impl* std*::*clone*::**Clone* *for* *Number* *{*
*fn* *clone**(**&**self**)* -> *Self* *{*
*Self* *{* ..*\***self* *}*
*}*
*}*
When invoking trait methods, the receiver is borrowed implicitly:
Rust code
*fn* *main**(**)* *{*
*let* n = *Number* *{* *odd**:* *true**,* *value**:* *51* *}**;*
*let* *mut* m = n*.**clone**(**)**;*
m*.**value* += *100**;*
*print\_number**(**&*n*)**;*
*print\_number**(**&*m*)**;*
*}*
To highlight this: these are equivalent:
Rust code
*let* m = n*.**clone**(**)**;*
*let* m = std*::*clone*::**Clone**::**clone**(**&*n*)**;*
Marker traits like `Copy` have no methods:
Rust code
*// note: \`Copy\` requires that \`Clone\` is implemented too*
*impl* std*::*clone*::**Clone* *for* *Number* *{*
*fn* *clone**(**&**self**)* -> *Self* *{*
*Self* *{* ..*\***self* *}*
*}*
*}*
*impl* std*::*marker*::**Copy* *for* *Number* *{**}*
Now, `Clone` can still be used:
Rust code
*fn* *main**(**)* *{*
*let* n = *Number* *{* *odd**:* *true**,* *value**:* *51* *}**;*
*let* m = n*.**clone**(**)**;*
*let* o = n*.**clone**(**)**;*
*}*
But `Number` values will no longer be moved:
Rust code
*fn* *main**(**)* *{*
*let* n = *Number* *{* *odd**:* *true**,* *value**:* *51* *}**;*
*let* m = n*;* *// \`m\` is a copy of \`n\`*
*let* o = n*;* *// same. \`n\` is neither moved nor borrowed.*
*}*
Some traits are so common, they can be implemented automatically by using the `derive` attribute:
Rust code
*#*\[*derive*(*Clone, Copy*)**\]**
*struct* *Number* *{*
*odd**:* *bool**,*
*value**:* *i32**,*
*}*
*// this expands to \`impl Clone for Number\` and \`impl Copy for Number\` blocks.*
Functions can be generic:
Rust code
*fn* *foobar**<**T**\>**(**arg**:* *T**)* *{*
*// do something with \`arg\`*
*}*
They can have multiple *type parameters*, which can then be used in the function's declaration and its body, instead of concrete types:
Rust code
*fn* *foobar**<**L**,* *R**\>**(**left**:* *L**,* *right**:* *R**)* *{*
*// do something with \`left\` and \`right\`*
*}*
Type parameters usually have *constraints*, so you can actually do something with them.
The simplest constraints are just trait names:
Rust code
*fn* *print**<**T**:* *Display**\>**(**value**:* *T**)* *{*
*println**!**(**"value = {}"*, value*)**;*
*}*
*fn* *print**<**T**:* *Debug**\>**(**value**:* *T**)* *{*
*println**!**(**"value = {:?}"*, value*)**;*
*}*
There's a longer syntax for type parameter constraints:
Rust code
*fn* *print**<**T**\>**(**value**:* *T**)*
*where*
*T**:* *Display**,*
*{*
*println**!**(**"value = {}"*, value*)**;*
*}*
Constraints can be more complicated: they can require a type parameter to implement multiple traits:
Rust code
*use* std*::*fmt*::*Debug*;*
*fn* *compare**<**T**\>**(**left**:* *T**,* *right**:* *T**)*
*where*
*T**:* *Debug* + *PartialEq**,*
*{*
*println**!**(**"{:?} {} {:?}"*, left, *if* left == right *{* *"=="* *}* else *{* *"!="* *}*, right*)**;*
*}*
*fn* *main**(**)* *{*
*compare**(**"tea"**,* *"coffee"**)**;*
*// prints: "tea" != "coffee"*
*}*
Generic functions can be thought of as namespaces, containing an infinity of functions with different concrete types.
Same as with crates, and modules, and types, generic functions can be "explored" (navigated?) using `::`
Rust code
*fn* *main**(**)* *{*
*use* std*::*any*::*type\_name*;*
*println**!**(**"{}"*, type\_name::<*i32*\>*(**)**)**;* *// prints "i32"*
*println**!**(**"{}"*, type\_name::<*(**f64*, *char**)*\>*(**)**)**;* *// prints "(f64, char)"*
*}*
This is lovingly called [turbofish syntax](https://turbo.fish/), because `::<>` looks like a fish.
Structs can be generic too:
Rust code
*struct* *Pair**<**T**\>* *{*
*a**:* *T**,*
*b**:* *T**,*
*}*
*fn* *print\_type\_name**<**T**\>**(**\_val**:* *&**T**)* *{*
*println**!**(**"{}"*, std::any::type\_name::<T>*(**)**)**;*
*}*
*fn* *main**(**)* *{*
*let* p1 = *Pair* *{* *a**:* *3**,* *b**:* *9* *}**;*
*let* p2 = *Pair* *{* *a**:* *true**,* *b**:* *false* *}**;*
*print\_type\_name**(**&*p1*)**;* *// prints "Pair<i32>"*
*print\_type\_name**(**&*p2*)**;* *// prints "Pair<bool>"*
*}*
The standard library type `Vec` (~ a heap-allocated array), is generic:
Rust code
*fn* *main**(**)* *{*
*let* *mut* v1 = *Vec**::**new**(**)**;*
v1*.**push**(**1**)**;*
*let* *mut* v2 = *Vec**::**new**(**)**;*
v2*.**push**(**false**)**;*
*print\_type\_name**(**&*v1*)**;* *// prints "Vec<i32>"*
*print\_type\_name**(**&*v2*)**;* *// prints "Vec<bool>"*
*}*
Speaking of `Vec`, it comes with a macro that gives more or less "vec literals":
Rust code
*fn* *main**(**)* *{*
*let* v1 = *vec**!**\[**1*, *2*, *3**\]**;*
*let* v2 = *vec**!**\[**true*, *false*, *true**\]**;*
*print\_type\_name**(**&*v1*)**;* *// prints "Vec<i32>"*
*print\_type\_name**(**&*v2*)**;* *// prints "Vec<bool>"*
*}*
All of `name!()`, `name![]` or `name!{}` invoke a macro. Macros just expand to regular code.
In fact, `println` is a macro:
Rust code
*fn* *main**(**)* *{*
*println**!**(**"{}"*, *"Hello there!"**)**;*
*}*
This expands to something that has the same effect as:
Rust code
*fn* *main**(**)* *{*
*use* std*::*io*::**{**self**,* Write*}**;*
io*::**stdout**(**)**.**lock**(**)**.**write\_all**(**b"Hello there!\\n"**)**.**unwrap**(**)**;*
*}*
`panic` is also a macro. It violently stops execution with an error message, and the file name / line number of the error, if enabled:
Rust code
*fn* *main**(**)* *{*
*panic**!**(**"This panics"**)**;*
*}*
*// output: thread 'main' panicked at 'This panics', src/main.rs:3:5*
Some methods also panic. For example, the `Option` type can contain something, or it can contain nothing. If `.unwrap()` is called on it, and it contains nothing, it panics:
Rust code
*fn* *main**(**)* *{*
*let* o1*:* *Option**<**i32**\>* = Some*(**128**)**;*
o1*.**unwrap**(**)**;* *// this is fine*
*let* o2*:* *Option**<**i32**\>* = None*;*
o2*.**unwrap**(**)**;* *// this panics!*
*}*
*// output: thread 'main' panicked at 'called \`Option::unwrap()\` on a \`None\` value', src/libcore/option.rs:378:21*
`Option` is not a struct - it's an `enum`, with two variants.
Rust code
*enum* *Option**<**T**\>* *{*
None*,*
Some*(**T**)**,*
*}*
*impl**<**T**\>* *Option**<**T**\>* *{*
*fn* *unwrap**(**self**)* -> *T* *{*
*// enums variants can be used in patterns:*
*match* *self* *{*
*Self**::*Some*(*t*)* => t*,*
*Self**::*None => *panic**!**(**".unwrap() called on a None option"**)**,*
*}*
*}*
*}*
*use* *self**::*Option*::**{*None*,* Some*}**;*
*fn* *main**(**)* *{*
*let* o1*:* *Option**<**i32**\>* = Some*(**128**)**;*
o1*.**unwrap**(**)**;* *// this is fine*
*let* o2*:* *Option**<**i32**\>* = None*;*
o2*.**unwrap**(**)**;* *// this panics!*
*}*
*// output: thread 'main' panicked at '.unwrap() called on a None option', src/main.rs:11:27*
`Result` is also an enum, it can either contain something, or an error:
Rust code
*enum* *Result**<**T**,* *E**\>* *{*
Ok*(**T**)**,*
Err*(**E**)**,*
*}*
It also panics when unwrapped and containing an error.
Variables bindings have a "lifetime":
Rust code
*fn* *main**(**)* *{*
*// \`x\` doesn't exist yet*
*{*
*let* x = *42**;* *// \`x\` starts existing*
*println**!**(**"x = {}"*, x*)**;*
*// \`x\` stops existing*
*}*
*// \`x\` no longer exists*
*}*
Similarly, references have a lifetime:
Rust code
*fn* *main**(**)* *{*
*// \`x\` doesn't exist yet*
*{*
*let* x = *42**;* *// \`x\` starts existing*
*let* x\_ref = *&*x*;* *// \`x\_ref\` starts existing - it borrows \`x\`*
*println**!**(**"x\_ref = {}"*, x\_ref*)**;*
*// \`x\_ref\` stops existing*
*// \`x\` stops existing*
*}*
*// \`x\` no longer exists*
*}*
The lifetime of a reference cannot exceed the lifetime of the variable binding it borrows:
Rust code
*fn* *main**(**)* *{*
*let* x\_ref = *{*
*let* x = *42**;*
*&*x
*}**;*
*println**!**(**"x\_ref = {}"*, x\_ref*)**;*
*// error: \`x\` does not live long enough*
*}*
A variable binding can be immutably borrowed multiple times:
Rust code
*fn* *main**(**)* *{*
*let* x = *42**;*
*let* x\_ref1 = *&*x*;*
*let* x\_ref2 = *&*x*;*
*let* x\_ref3 = *&*x*;*
*println**!**(**"{} {} {}"*, x\_ref1, x\_ref2, x\_ref3*)**;*
*}*
While borrowed, a variable binding cannot be mutated:
Rust code
*fn* *main**(**)* *{*
*let* *mut* x = *42**;*
*let* x\_ref = *&*x*;*
x = *13**;*
*println**!**(**"x\_ref = {}"*, x\_ref*)**;*
*// error: cannot assign to \`x\` because it is borrowed*
*}*
While immutably borrowed, a variable cannot be *mutably borrowed*:
Rust code
*fn* *main**(**)* *{*
*let* *mut* x = *42**;*
*let* x\_ref1 = *&*x*;*
*let* x\_ref2 = *&**mut* x*;*
*// error: cannot borrow \`x\` as mutable because it is also borrowed as immutable*
*println**!**(**"x\_ref1 = {}"*, x\_ref1*)**;*
*}*
References in function arguments also have lifetimes:
Rust code
*fn* *print**(**x**:* *&**i32**)* *{*
*// \`x\` is borrowed (from the outside) for the*
*// entire time this function is called.*
*}*
Functions with reference arguments can be called with borrows that have different lifetimes, so:
- All functions that take references are generic
- Lifetimes are generic parameters
Lifetimes' names start with a single quote, `'`:
Rust code
*// elided (non-named) lifetimes:*
*fn* *print**(**x**:* *&**i32**)* *{**}*
*// named lifetimes:*
*fn* *print**<**'**a**\>**(**x**:* *&**'**a* *i32**)* *{**}*
This allows returning references whose lifetime depend on the lifetime of the arguments:
Rust code
*struct* *Number* *{*
*value**:* *i32**,*
*}*
*fn* *number\_value**<**'**a**\>**(**num**:* *&**'**a* *Number**)* -> *&**'**a* *i32* *{*
*&*num*.**value*
*}*
*fn* *main**(**)* *{*
*let* n = *Number* *{* *value**:* *47* *}**;*
*let* v = *number\_value**(**&*n*)**;*
*// \`v\` borrows \`n\` (immutably), thus: \`v\` cannot outlive \`n\`.*
*// While \`v\` exists, \`n\` cannot be mutably borrowed, mutated, moved, etc.*
*}*
When there is a *single* input lifetime, it doesn't need to be named, and everything has the same lifetime, so the two functions below are equivalent:
Rust code
*fn* *number\_value**<**'**a**\>**(**num**:* *&**'**a* *Number**)* -> *&**'**a* *i32* *{*
*&*num*.**value*
*}*
*fn* *number\_value**(**num**:* *&**Number**)* -> *&**i32* *{*
*&*num*.**value*
*}*
Structs can also be *generic over lifetimes*, which allows them to hold references:
Rust code
*struct* *NumRef**<**'**a**\>* *{*
*x**:* *&**'**a* *i32**,*
*}*
*fn* *main**(**)* *{*
*let* x*:* *i32* = *99**;*
*let* x\_ref = *NumRef* *{* *x**:* *&*x *}**;*
*// \`x\_ref\` cannot outlive \`x\`, etc.*
*}*
The same code, but with an additional function:
Rust code
*struct* *NumRef**<**'**a**\>* *{*
*x**:* *&**'**a* *i32**,*
*}*
*fn* *as\_num\_ref**<**'**a**\>**(**x**:* *&**'**a* *i32**)* -> *NumRef**<**'**a**\>* *{*
*NumRef* *{* *x**:* *&*x *}*
*}*
*fn* *main**(**)* *{*
*let* x*:* *i32* = *99**;*
*let* x\_ref = *as\_num\_ref**(**&*x*)**;*
*// \`x\_ref\` cannot outlive \`x\`, etc.*
*}*
The same code, but with "elided" lifetimes:
Rust code
*struct* *NumRef**<**'**a**\>* *{*
*x**:* *&**'**a* *i32**,*
*}*
*fn* *as\_num\_ref**(**x**:* *&**i32**)* -> *NumRef**<**'**\_**\>* *{*
*NumRef* *{* *x**:* *&*x *}*
*}*
*fn* *main**(**)* *{*
*let* x*:* *i32* = *99**;*
*let* x\_ref = *as\_num\_ref**(**&*x*)**;*
*// \`x\_ref\` cannot outlive \`x\`, etc.*
*}*
`impl` blocks can be generic over lifetimes too:
Rust code
*impl**<**'**a**\>* *NumRef**<**'**a**\>* *{*
*fn* *as\_i32\_ref**(**&**'**a* *self**)* -> *&**'**a* *i32* *{*
*self**.**x*
*}*
*}*
*fn* *main**(**)* *{*
*let* x*:* *i32* = *99**;*
*let* x\_num\_ref = *NumRef* *{* *x**:* *&*x *}**;*
*let* x\_i32\_ref = x\_num\_ref*.**as\_i32\_ref**(**)**;*
*// neither ref can outlive \`x\`*
*}*
But you can do elision ("to elide") there too:
Rust code
*impl**<**'**a**\>* *NumRef**<**'**a**\>* *{*
*fn* *as\_i32\_ref**(**&**self**)* -> *&**i32* *{*
*self**.**x*
*}*
*}*
You can elide even harder, if you never need the name:
Rust code
*impl* *NumRef**<**'**\_**\>* *{*
*fn* *as\_i32\_ref**(**&**self**)* -> *&**i32* *{*
*self**.**x*
*}*
*}*
There is a special lifetime, named `'static`, which is valid for the entire program's lifetime.
String literals are `'static`:
Rust code
*struct* *Person* *{*
*name**:* *&**'**static* *str**,*
*}*
*fn* *main**(**)* *{*
*let* p = *Person* *{*
*name**:* *"fasterthanlime"**,*
*}**;*
*}*
But *owned strings* are not static:
Rust code
*struct* *Person* *{*
*name**:* *&**'**static* *str**,*
*}*
*fn* *main**(**)* *{*
*let* name = *format**!**(**"fasterthan{}"*, *"lime"**)**;*
*let* p = *Person* *{* *name**:* *&*name *}**;*
*// error: \`name\` does not live long enough*
*}*
In that last example, the local `name` is not a `&'static str`, it's a `String`. It's been allocated dynamically, and it will be freed. Its lifetime is *less* than the whole program (even though it happens to be in `main`).
To store a non-`'static` string in `Person`, it needs to either:
A) Be generic over a lifetime:
Rust code
*struct* *Person**<**'**a**\>* *{*
*name**:* *&**'**a* *str**,*
*}*
*fn* *main**(**)* *{*
*let* name = *format**!**(**"fasterthan{}"*, *"lime"**)**;*
*let* p = *Person* *{* *name**:* *&*name *}**;*
*// \`p\` cannot outlive \`name\`*
*}*
or
B) Take ownership of the string
Rust code
*struct* *Person* *{*
*name**:* *String**,*
*}*
*fn* *main**(**)* *{*
*let* name = *format**!**(**"fasterthan{}"*, *"lime"**)**;*
*let* p = *Person* *{* *name**:* name *}**;*
*// \`name\` was moved into \`p\`, their lifetimes are no longer tied.*
*}*
Speaking of: in a struct literal, when a field is set to a variable binding of the same name:
Rust code
*let* p = *Person* *{* *name**:* name *}**;*
It can be shortened like this:
Rust code
*let* p = *Person* *{* name *}**;*
For many types in Rust, there are owned and non-owned variants:
- Strings: `String` is owned, `&str` is a reference
- Paths: `PathBuf` is owned, `&Path` is a reference
- Collections: `Vec<T>` is owned, `&[T]` is a reference
Rust has slices - they're a reference to multiple contiguous elements.
You can borrow a slice of a vector, for example:
Rust code
*fn* *main**(**)* *{*
*let* v = *vec**!**\[**1*, *2*, *3*, *4*, *5**\]**;*
*let* v2 = *&*v*\[**2*..*4**\]**;*
*println**!**(**"v2 = {:?}"*, v2*)**;*
*}*
*// output:*
*// v2 = \[3, 4\]*
The above is not magical. The indexing operator (`foo[index]`) is overloaded with the `Index` and `IndexMut` traits.
The `..` syntax is just range literals. Ranges are just a few structs defined in the standard library.
They can be open-ended, and their rightmost bound can be inclusive, if it's preceded by `=`.
Rust code
*fn* *main**(**)* *{*
*// 0 or greater*
*println**!**(**"{:?}"*, *(**0*..*)*.contains*(*&*100**)**)**;* *// true*
*// strictly less than 20*
*println**!**(**"{:?}"*, *(*..*20**)*.contains*(*&*20**)**)**;* *// false*
*// 20 or less than 20*
*println**!**(**"{:?}"*, *(*..=*20**)*.contains*(*&*20**)**)**;* *// true*
*// only 3, 4, 5*
*println**!**(**"{:?}"*, *(**3*..*6**)*.contains*(*&*4**)**)**;* *// true*
*}*
Borrowing rules apply to slices.
Rust code
*fn* *tail**(**s**:* *&**\[**u8**\]**)* -> *&**\[**u8**\]* *{*
*&*s*\[**1*..*\]*
*}*
*fn* *main**(**)* *{*
*let* x = *&**\[**1**,* *2**,* *3**,* *4**,* *5**\]**;*
*let* y = *tail**(*x*)**;*
*println**!**(**"y = {:?}"*, y*)**;*
*}*
This is the same as:
Rust code
*fn* *tail**<**'**a**\>**(**s**:* *&**'**a* *\[**u8**\]**)* -> *&**'**a* *\[**u8**\]* *{*
*&*s*\[**1*..*\]*
*}*
This is legal:
Rust code
*fn* *main**(**)* *{*
*let* y = *{*
*let* x = *&**\[**1**,* *2**,* *3**,* *4**,* *5**\]**;*
*tail**(*x*)*
*}**;*
*println**!**(**"y = {:?}"*, y*)**;*
*}*
...but only because `[1, 2, 3, 4, 5]` is a `'static` array.
So, this is illegal:
Rust code
*fn* *main**(**)* *{*
*let* y = *{*
*let* v = *vec**!**\[**1*, *2*, *3*, *4*, *5**\]**;*
*tail**(**&*v*)*
*// error: \`v\` does not live long enough*
*}**;*
*println**!**(**"y = {:?}"*, y*)**;*
*}*
...because a vector is heap-allocated, and it has a non-`'static` lifetime.
`&str` values are really slices.
Rust code
*fn* *file\_ext**(**name**:* *&**str**)* -> *Option**<**&**str**\>* *{*
*// this does not create a new string - it returns*
*// a slice of the argument.*
name*.**split**(**"."**)**.**last**(**)*
*}*
*fn* *main**(**)* *{*
*let* name = *"Read me. Or don't.txt"**;*
*if* *let* Some*(*ext*)* = *file\_ext**(*name*)* *{*
*println**!**(**"file extension: {}"*, ext*)**;*
*}* *else* *{*
*println**!**(**"no file extension"**)**;*
*}*
*}*
...so the borrow rules apply here too:
Rust code
*fn* *main**(**)* *{*
*let* ext = *{*
*let* name = *String**::**from**(**"Read me. Or don't.txt"**)**;*
*file\_ext**(**&*name*)**.**unwrap\_or**(**""**)*
*// error: \`name\` does not live long enough*
*}**;*
*println**!**(**"extension: {:?}"*, ext*)**;*
*}*
Functions that can fail typically return a `Result`:
Rust code
*fn* *main**(**)* *{*
*let* s = std*::*str*::**from\_utf8**(**&**\[**240**,* *159**,* *141**,* *137**\]**)**;*
*println**!**(**"{:?}"*, s*)**;*
*// prints: Ok("🍉")*
*let* s = std*::*str*::**from\_utf8**(**&**\[**195**,* *40**\]**)**;*
*println**!**(**"{:?}"*, s*)**;*
*// prints: Err(Utf8Error { valid\_up\_to: 0, error\_len: Some(1) })*
*}*
If you want to panic in case of failure, you can `.unwrap()`:
Rust code
*fn* *main**(**)* *{*
*let* s = std*::*str*::**from\_utf8**(**&**\[**240**,* *159**,* *141**,* *137**\]**)**.**unwrap**(**)**;*
*println**!**(**"{:?}"*, s*)**;*
*// prints: "🍉"*
*let* s = std*::*str*::**from\_utf8**(**&**\[**195**,* *40**\]**)**.**unwrap**(**)**;*
*// prints: thread 'main' panicked at 'called \`Result::unwrap()\`*
*// on an \`Err\` value: Utf8Error { valid\_up\_to: 0, error\_len: Some(1) }',*
*// src/libcore/result.rs:1165:5*
*}*
Or `.expect()`, for a custom message:
Rust code
*fn* *main**(**)* *{*
*let* s = std*::*str*::**from\_utf8**(**&**\[**195**,* *40**\]**)**.**expect**(**"valid utf-8"**)**;*
*// prints: thread 'main' panicked at 'valid utf-8: Utf8Error*
*// { valid\_up\_to: 0, error\_len: Some(1) }', src/libcore/result.rs:1165:5*
*}*
Or, you can `match`:
Rust code
*fn* *main**(**)* *{*
*match* std*::*str*::**from\_utf8**(**&**\[**240**,* *159**,* *141**,* *137**\]**)* *{*
Ok*(*s*)* => *println**!**(**"{}"*, s*)**,*
Err*(*e*)* => *panic**!**(*e*)**,*
*}*
*// prints 🍉*
*}*
Or you can `if let`:
Rust code
*fn* *main**(**)* *{*
*if* *let* Ok*(*s*)* = std*::*str*::**from\_utf8**(**&**\[**240**,* *159**,* *141**,* *137**\]**)* *{*
*println**!**(**"{}"*, s*)**;*
*}*
*// prints 🍉*
*}*
Or you can bubble up the error:
Rust code
*fn* *main**(**)* -> *Result**<**(**)**,* std*::*str*::**Utf8Error**\>* *{*
*match* std*::*str*::**from\_utf8**(**&**\[**240**,* *159**,* *141**,* *137**\]**)* *{*
Ok*(*s*)* => *println**!**(**"{}"*, s*)**,*
Err*(*e*)* => *return* Err*(*e*)**,*
*}*
Ok*(**(**)**)*
*}*
Or you can use `?` to do it the concise way:
Rust code
*fn* *main**(**)* -> *Result**<**(**)**,* std*::*str*::**Utf8Error**\>* *{*
*let* s = std*::*str*::**from\_utf8**(**&**\[**240**,* *159**,* *141**,* *137**\]**)*?*;*
*println**!**(**"{}"*, s*)**;*
Ok*(**(**)**)*
*}*
The `*` operator can be used to *dereference*, but you don't need to do that to access fields or call methods:
Rust code
*struct* *Point* *{*
*x**:* *f64**,*
*y**:* *f64**,*
*}*
*fn* *main**(**)* *{*
*let* p = *Point* *{* *x**:* *1.0**,* *y**:* *3.0* *}**;*
*let* p\_ref = *&*p*;*
*println**!**(**"({}, {})"*, p\_ref.x, p\_ref.y*)**;*
*}*
*// prints \`(1, 3)\`*
And you can only do it if the type is `Copy`:
Rust code
*struct* *Point* *{*
*x**:* *f64**,*
*y**:* *f64**,*
*}*
*fn* *negate**(**p**:* *Point**)* -> *Point* *{*
*Point* *{*
*x**:* -p*.**x**,*
*y**:* -p*.**y**,*
*}*
*}*
*fn* *main**(**)* *{*
*let* p = *Point* *{* *x**:* *1.0**,* *y**:* *3.0* *}**;*
*let* p\_ref = *&*p*;*
*negate**(**\**p\_ref*)**;*
*// error: cannot move out of \`\*p\_ref\` which is behind a shared reference*
*}*
Rust code
*// now \`Point\` is \`Copy\`*
*#*\[*derive*(*Clone, Copy*)**\]**
*struct* *Point* *{*
*x**:* *f64**,*
*y**:* *f64**,*
*}*
*fn* *negate**(**p**:* *Point**)* -> *Point* *{*
*Point* *{*
*x**:* -p*.**x**,*
*y**:* -p*.**y**,*
*}*
*}*
*fn* *main**(**)* *{*
*let* p = *Point* *{* *x**:* *1.0**,* *y**:* *3.0* *}**;*
*let* p\_ref = *&*p*;*
*negate**(**\**p\_ref*)**;* *// ...and now this works*
*}*
Closures are just functions of type `Fn`, `FnMut` or `FnOnce` with some captured context.
Their parameters are a comma-separated list of names within a pair of pipes (`|`). They don't *need* curly braces, unless you want to have multiple statements.
Rust code
*fn* *for\_each\_planet**<**F**\>**(**f**:* *F**)*
*where* *F**:* *Fn**(**&**'**static* *str**)*
*{*
*f**(**"Earth"**)**;*
*f**(**"Mars"**)**;*
*f**(**"Jupiter"**)**;*
*}*
*fn* *main**(**)* *{*
*for\_each\_planet**(*|planet| *println**!**(**"Hello, {}"*, planet*)**)**;*
*}*
*// prints:*
*// Hello, Earth*
*// Hello, Mars*
*// Hello, Jupiter*
The borrow rules apply to them too:
Rust code
*fn* *for\_each\_planet**<**F**\>**(**f**:* *F**)*
*where* *F**:* *Fn**(**&**'**static* *str**)*
*{*
*f**(**"Earth"**)**;*
*f**(**"Mars"**)**;*
*f**(**"Jupiter"**)**;*
*}*
*fn* *main**(**)* *{*
*let* greeting = *String**::**from**(**"Good to see you"**)**;*
*for\_each\_planet**(*|planet| *println**!**(**"{}, {}"*, greeting, planet*)**)**;*
*// our closure borrows \`greeting\`, so it cannot outlive it*
*}*
For example, this would not work:
Rust code
*fn* *for\_each\_planet**<**F**\>**(**f**:* *F**)*
*where* *F**:* *Fn**(**&**'**static* *str**)* + *'**static* *// \`F\` must now have "'static" lifetime*
*{*
*f**(**"Earth"**)**;*
*f**(**"Mars"**)**;*
*f**(**"Jupiter"**)**;*
*}*
*fn* *main**(**)* *{*
*let* greeting = *String**::**from**(**"Good to see you"**)**;*
*for\_each\_planet**(*|planet| *println**!**(**"{}, {}"*, greeting, planet*)**)**;*
*// error: closure may outlive the current function, but it borrows*
*// \`greeting\`, which is owned by the current function*
*}*
But this would:
Rust code
*fn* *main**(**)* *{*
*let* greeting = *String**::**from**(**"You're doing great"**)**;*
*for\_each\_planet**(**move* |planet| *println**!**(**"{}, {}"*, greeting, planet*)**)**;*
*// \`greeting\` is no longer borrowed, it is \*moved\* into*
*// the closure.*
*}*
An `FnMut` needs to be mutably borrowed to be called, so it can only be called once at a time.
This is legal:
Rust code
*fn* *foobar**<**F**\>**(**f**:* *F**)*
*where* *F**:* *Fn**(**i32**)* -> *i32*
*{*
*println**!**(**"{}"*, f*(*f*(**2**)**)**)**;*
*}*
*fn* *main**(**)* *{*
*foobar**(*|x| x *\** *2**)**;*
*}*
*// output: 8*
This isn't:
Rust code
*fn* *foobar**<**F**\>**(**mut* *f**:* *F**)*
*where* *F**:* *FnMut**(**i32**)* -> *i32*
*{*
*println**!**(**"{}"*, f*(*f*(**2**)**)**)**;*
*// error: cannot borrow \`f\` as mutable more than once at a time*
*}*
*fn* *main**(**)* *{*
*foobar**(*|x| x *\** *2**)**;*
*}*
This is legal again:
Rust code
*fn* *foobar**<**F**\>**(**mut* *f**:* *F**)*
*where* *F**:* *FnMut**(**i32**)* -> *i32*
*{*
*let* tmp = *f**(**2**)**;*
*println**!**(**"{}"*, f*(*tmp*)**)**;*
*}*
*fn* *main**(**)* *{*
*foobar**(*|x| x *\** *2**)**;*
*}*
*// output: 8*
`FnMut` exists because some closures *mutably borrow* local variables:
Rust code
*fn* *foobar**<**F**\>**(**mut* *f**:* *F**)*
*where* *F**:* *FnMut**(**i32**)* -> *i32*
*{*
*let* tmp = *f**(**2**)**;*
*println**!**(**"{}"*, f*(*tmp*)**)**;*
*}*
*fn* *main**(**)* *{*
*let* *mut* acc = *2**;*
*foobar**(*|x| *{*
acc += *1**;*
x *\** acc
*}**)**;*
*}*
*// output: 24*
Those closures cannot be passed to functions expecting `Fn`:
Rust code
*fn* *foobar**<**F**\>**(**f**:* *F**)*
*where* *F**:* *Fn**(**i32**)* -> *i32*
*{*
*println**!**(**"{}"*, f*(*f*(**2**)**)**)**;*
*}*
*fn* *main**(**)* *{*
*let* *mut* acc = *2**;*
*foobar**(*|x| *{*
acc += *1**;*
*// error: cannot assign to \`acc\`, as it is a*
*// captured variable in a \`Fn\` closure.*
*// the compiler suggests "changing foobar*
*// to accept closures that implement \`FnMut\`"*
x *\** acc
*}**)**;*
*}*
`FnOnce` closures can only be called once. They exist because some closure move out variables that have been moved when captured:
Rust code
*fn* *foobar**<**F**\>**(**f**:* *F**)*
*where* *F**:* *FnOnce**(**)* -> *String*
*{*
*println**!**(**"{}"*, f*(**)**)**;*
*}*
*fn* *main**(**)* *{*
*let* s = *String**::**from**(**"alright"**)**;*
*foobar**(**move* || s*)**;*
*// \`s\` was moved into our closure, and our*
*// closures moves it to the caller by returning*
*// it. Remember that \`String\` is not \`Copy\`.*
*}*
This is enforced naturally, as `FnOnce` closures need to be *moved* in order to be called.
So, for example, this is illegal:
Rust code
*fn* *foobar**<**F**\>**(**f**:* *F**)*
*where* *F**:* *FnOnce**(**)* -> *String*
*{*
*println**!**(**"{}"*, f*(**)**)**;*
*println**!**(**"{}"*, f*(**)**)**;*
*// error: use of moved value: \`f\`*
*}*
And, if you need convincing that our closure *does* move `s`, this is illegal too:
Rust code
*fn* *main**(**)* *{*
*let* s = *String**::**from**(**"alright"**)**;*
*foobar**(**move* || s*)**;*
*foobar**(**move* || s*)**;*
*// use of moved value: \`s\`*
*}*
But this is fine:
Rust code
*fn* *main**(**)* *{*
*let* s = *String**::**from**(**"alright"**)**;*
*foobar**(*|| s*.**clone**(**)**)**;*
*foobar**(*|| s*.**clone**(**)**)**;*
*}*
Here's a closure with two arguments:
Rust code
*fn* *foobar**<**F**\>**(**x**:* *i32**,* *y**:* *i32**,* *is\_greater**:* *F**)*
*where* *F**:* *Fn**(**i32**,* *i32**)* -> *bool*
*{*
*let* *(*greater*,* smaller*)* = *if* *is\_greater**(*x*,* y*)* *{*
*(*x*,* y*)*
*}* *else* *{*
*(*y*,* x*)*
*}**;*
*println**!**(**"{} is greater than {}"*, greater, smaller*)**;*
*}*
*fn* *main**(**)* *{*
*foobar**(**32**,* *64**,* |x*,* y| x > y*)**;*
*}*
Here's a closure ignoring both its arguments:
Rust code
*fn* *main**(**)* *{*
*foobar**(**32**,* *64**,* |\_*,* \_| *panic**!**(**"Comparing is futile!"**)**)**;*
*}*
Here's a slightly worrying closure:
Rust code
*fn* *countdown**<**F**\>**(**count**:* *usize**,* *tick**:* *F**)*
*where* *F**:* *Fn**(**usize**)*
*{*
*for* i *in* *(**1*..=count*)**.**rev**(**)* *{*
*tick**(*i*)**;*
*}*
*}*
*fn* *main**(**)* *{*
*countdown**(**3**,* |i| *println**!**(**"tick {}..."*, i*)**)**;*
*}*
*// output:*
*// tick 3...*
*// tick 2...*
*// tick 1...*
And here's a toilet closure:
Rust code
*fn* *main**(**)* *{*
*countdown**(**3**,* |\_| *(**)**)**;*
*}*
Called thusly because `|_| ()` looks like a toilet.
Anything that is iterable can be used in a `for in` loop.
We've just seen a range being used, but it also works with a `Vec`:
Rust code
*fn* *main**(**)* *{*
*for* i *in* *vec**!**\[**52*, *49*, *21**\]* *{*
*println**!**(**"I like the number {}"*, i*)**;*
*}*
*}*
Or a slice:
Rust code
*fn* *main**(**)* *{*
*for* i *in* *&**\[**52**,* *49**,* *21**\]* *{*
*println**!**(**"I like the number {}"*, i*)**;*
*}*
*}*
*// output:*
*// I like the number 52*
*// I like the number 49*
*// I like the number 21*
Or an actual iterator:
Rust code
*fn* *main**(**)* *{*
*// note: \`&str\` also has a \`.bytes()\` iterator.*
*// Rust's \`char\` type is a "Unicode scalar value"*
*for* c *in* *"rust"**.**chars**(**)* *{*
*println**!**(**"Give me a {}"*, c*)**;*
*}*
*}*
*// output:*
*// Give me a r*
*// Give me a u*
*// Give me a s*
*// Give me a t*
Even if the iterator items are filtered and mapped and flattened:
Rust code
*fn* *main**(**)* *{*
*for* c *in* *"SuRPRISE INbOUND"*
*.**chars**(**)*
*.**filter**(*|c| c*.**is\_lowercase**(**)**)*
*.**flat\_map**(*|c| c*.**to\_uppercase**(**)**)*
*{*
*print**!**(**"{}"*, c*)**;*
*}*
*println**!**(**)**;*
*}*
*// output: UB*
You can return a closure from a function:
Rust code
*fn* *make\_tester**(**answer**:* *String**)* -> *impl* *Fn**(**&**str**)* -> *bool* *{*
*move* |challenge| *{*
challenge == answer
*}*
*}*
*fn* *main**(**)* *{*
*// you can use \`.into()\` to perform conversions*
*// between various types, here \`&'static str\` and \`String\`*
*let* test = *make\_tester**(**"hunter2"**.**into**(**)**)**;*
*println**!**(**"{}"*, test*(**"\*\*\*\*\*\*"**)**)**;*
*println**!**(**"{}"*, test*(**"hunter2"**)**)**;*
*}*
You can even move a reference to some of a function's arguments, into a closure it returns:
Rust code
*fn* *make\_tester**<**'**a**\>**(**answer**:* *&**'**a* *str**)* -> *impl* *Fn**(**&**str**)* -> *bool* + *'**a* *{*
*move* |challenge| *{*
challenge == answer
*}*
*}*
*fn* *main**(**)* *{*
*let* test = *make\_tester**(**"hunter2"**)**;*
*println**!**(**"{}"*, test*(**"\*\*\*\*\*\*\*"**)**)**;*
*println**!**(**"{}"*, test*(**"hunter2"**)**)**;*
*}*
*// output:*
*// false*
*// true*
Or, with elided lifetimes:
Rust code
*fn* *make\_tester**(**answer**:* *&**str**)* -> *impl* *Fn**(**&**str**)* -> *bool* + *'**\_* *{*
*move* |challenge| *{*
challenge == answer
*}*
*}*
And with that, we hit the 30-minute estimated reading time mark, and you should be able to read *most* of the Rust code you find online.
Writing Rust is a very different experience from reading Rust. On one hand, you're not reading the *solution* to a problem, you're actually solving it. On the other hand, the Rust compiler helps out a *lot*.
For all of the intentional mistakes made above ("this code is illegal", etc.), rustc always has very good error messages *and* insightful suggestions.
And when there's a hint missing, the compiler team is [not afraid to add it](https://mobile.twitter.com/fasterthanlime/status/1219601989404954624).
For more Rust material, you may want to check out:
- [The Rust Book](https://doc.rust-lang.org/book/)
- [Rust By Example](https://doc.rust-lang.org/stable/rust-by-example/)
- [Read Rust](https://readrust.net/)
- [This Week In Rust](https://this-week-in-rust.org/)
I also [blog about Rust](https://fasterthanli.me/tags/rust/) and [tweet about Rust](https://twitter.com/fasterthanlime) a lot, so if you liked this article, you know what to do.
Have fun!