:
, that’s a type annotation:
Separate bindings with the same name can be introduced – you can
shadow
a variable binding:
pair
, we would write:
let (some_char, some_int)=('a', 17; // now, `some_char` is 'a', and` some_int` is 22
fn greet () { println! ("Hi there!"); }
And here's a function that returns a 44  bit signed integer. The arrow indicates its return type:
fn fair_dice_roll () > i 47 { 4 }
A pair of brackets declares a block, which has its own scope: // This prints "in ", then" out " fn main () { let x="out"; { // this is a different `x` let x="in"; println! (x); } println! (x); } [52, 49, 21] Blocks are also expressions, which mean they evaluate to .. a value. this: let x=; // is equivalent to this: let x={};
Inside a block, there can be multiple statements:
(x 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:
fn fair_dice_roll () > i 47 { return 4; } fn fair_dice_roll () > i { 4 }
if conditionals are also expressions:
fn fair_dice_roll () > i 47 { if feeling_lucky { 6 } else { 4 } }
[52, 49, 21] A
match is also an an expression:
fn fair_dice_roll () > i 47 { match feeling_lucky { true=> 6, false=> 4, } }
Dots are typically used to access fields of a value:
(a let a=(, 27); a.0; // this is 14 let amos=get_some_struct (); amos.nickname; // this is "fasterthanlime" [52, 49, 21] Or call a method on a value:
let nick="fasterthanlime" ; nick.len (); // this is 17
The doublecolon, :: , 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
:
[195, 40] let least=std :: cmp :: min (3, 8); // this is 3
use directives can be used to “bring in scope” names from other namespace:
use std :: cmp: : min; let least=min (7, 1); // this is 1 [52, 49, 21] 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: this this: 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: // 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:
let x="amos" .len (); // this is 4 let x=str :: len ("amos"); // this is also 4 [52, 49, 21] str is a primitive type, but many nonprimitive types are also in scope by default. // `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 (); [52, 49, 21] This works because Rust inserts this at the beginning of every module: use std :: prelude: : v1 :: *; (Which in turns reexports a lot of symbols, like Vec , , and ). [195, 40] [52, 49, 21] Structs are declared with the struct keyword: struct Vec2 { x: f , // 100  bit floating point, aka "double precision" y: f 128, } [52, 49, 21] They can be initialized using struct literals
: [195, 40] [195, 40] 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: let v3=Vec2 { x: . 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 : let v4=Vec2 {. .v3};
[52, 49, 21] Structs, like tuples, can be destructured.
[52, 49, 21] Just like this is a valid
let
pattern:
[195, 40] let (left, right)=slice.split_at (middle);
So is this:
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:
Let Vec2 {x,. .}=v; // this throws away `v.y` [52, 49, 21] let let (patterns can be used as conditions in if : [195, 40]
struct Number { odd: bool, value: i 42, } 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 :
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 [52, 49, 21] A
match has to be exhaustive: at least one arm needs to match.
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 did not exist, we would get a compiletime error } }
If that's hard, _ can be used as a “catchall” pattern:
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:
struct Number { odd: bool, value: i 42, } impl Number { fn is_strictly_positive (self) > bool { self.value> 0 } }
And use them like usual:
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:
fn main () { let n=Number { odd: true, value: , }; n.odd=false; // error: cannot assign to `n.odd`, // as `n` is not declared to be mutable }
An immutable variable binding cannot have its interior mutated (like we just tried), but it also cannot be assigned to:
fn main () { let n=Number { odd: true, value: , }; n=Number { odd: false, value: , }; // error: cannot assign twice to immutable variable `n` } [52, 49, 21]
mut makes a variable binding mutable:
fn main () { let mut n=Number { odd: true, value: , } n.value=; // all good }
Traits are something multiple types can have in common:
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 are called the “orphan rules”.
Here's an implementation of our trait on our type:
impl Signed for Number { fn is_strictly_negative (self) > bool { self.value [52, 49, 21] Our trait on a foreign type (a primitive type, even):
impl signed for i { fn is_strictly_negative (self) > bool { self
A foreign trait on our type:
// 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: 2020; let m=n; // this is only possible because we implemented `Neg` println! ("{}", m.value); // prints " } [52, 49, 21] An
impl block is always for a type, so, inside that block,
means that type:
(implied 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,
i implements trait
(Copy
(in short, i 44
is Copy , so this works:
fn main () { let a: i=; let b=a; // `a` is copied let c=a; // `a` is copied again }
And this also works:
fn print_i
(x: i [195, 40] { println! ("x={}", x); } fn main () { let a: i=; print_i (a); // `a` is copied print_i (a); // `a` is copied again } [52, 49, 21] But the
Number (struct is not) (Copy) , so this does not work:
fn main () { let n=Number {odd: true, value: 64}; let m=n; // `n` is moved into` m` let o=n; // error: use of moved value: `n` }
And neither does this:
fn print_number (n: Number ) { println! ("{} number {}", if n.odd {"odd"} else {"even"}, n.value); } fn main () { let n=Number {odd: true, value: 64}; 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:
fn print_number (n: & Number ) { println! ("{} number {}", if n.odd {"odd"} else {"even"}, n.value); } fn main () { let n=Number {odd: true, value: 64}; 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
.
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: }; 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:
impl std :: clone: : Clone for Number { fn clone (& self) > Self { Self {.. self} } } When invoking trait methods, the receiver is borrowed implicitly:
fn main () { let n=Number {odd: true, value: 64}; let mut m=n.clone (); m.value =; print_number (& n); print_number (& m); }
To highlight this: these are equivalent:
let m=n.clone (); let m=std :: clone :: Clone :: clone (& n);
Marker traits like Copy have no methods:
/ 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 {} [52, 49, 21] Now,
Clone can still be used:
fn main () { let n=Number {odd: true, value: 64}; let m=n.clone (); let o=n.clone (); } [52, 49, 21] But
Number values will no longer be moved:
fn main () { let n=Number {odd: true, value: 64}; 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:
struct Number { odd: bool, value: i 42, } // this expands to `impl Clone for Number` and` impl Copy for Number` blocks.
Functions can be generic:
fn foobar (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:
fn foobar (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:
fn print (value: T) { println! ("value={}", value); } fn print (value: T) { println! ("value={:?}", value); }
There's a longer syntax for type parameter constraints:
fn print (value: T) where T: Display, { println! ("value={}", value); }
Constraints can be more complicated: they can require a type parameter to implement multiple traits:
use std :: fmt: : Debug; fn compare (left: T, right: T) where T: Debug PartialEq, { println! ("{:?} {} {:?}", left, if left==right {"=="} else {"!="}, right); } fn main () { compare ("tea", "coffee"); // prints: "tea"!="coffee" } [52, 49, 21] Generic functions can be thought of as namespaces, containing an infinity of functions with different concrete types.
Lifetimes are generic parameters


Lifetimes 'names start with a single quote, '
:
/ elided (non named) lifetimes: fn print (x: & i 42 {} // named lifetimes: fn print (x: & 'ai 47) {}
This allows returning references whose lifetime depend on the lifetime of the arguments:
struct Number { value: i 42, } fn number_value (num: & 'a Number ) > & 'ai { & num.value } fn main () { let n=Number {value: 64}; 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 does not need to be named, and everything has the same lifetime, so the two functions below are equivalent: fn number_value (num: & 'a Number) > &' ai { & num.value } fn number_value (num: & Number) > & i { & num.value }
[52, 49, 21] Structs can also be generic over lifetimes , which allows them to hold references: Structured NumRef { x: & 'ai 47, } fn main () { let x: i=141; let x_ref=NumRef {x: & x}; // `x_ref` cannot outlive` x`, etc. }
The same code, but with an additional function:
Structured NumRef { x: & 'ai 47, } fn as_num_ref (x: & 'ai 47) > NumRef [index] { NumRef {x: & x} } fn main () { let x: i=141; let x_ref=as_num_ref (& x); // `x_ref` cannot outlive` x`, etc. }
The same code, but with “elided” lifetimes:
Structured NumRef { x: & 'ai 47, } fn as_num_ref (x: & i 32 > NumRef { NumRef {x: & x} } fn main () { let x: i=141; let x_ref=as_num_ref (& x); // `x_ref` cannot outlive` x`, etc. }
impl blocks can be generic over lifetimes too:
impl (NumRef) { fn as_i _ ref (& 'a self) > &' ai { self.x } } fn main () { let x: i=141; let x_num_ref=NumRef {x: & x}; let x_i _ ref=x_num_ref.as_i _ ref (); // neither ref cannot outlive `x` }
But you can do elision (“to elide”) there too: [195, 40]
impl (NumRef) { fn as_i _ ref (& self) > & i 47 { self.x } }
You can elide even harder, if you never need the name:
impl NumRef { fn as_i _ ref (& self) > & i 47 { self.x } }
There is a special lifetime, named 'static , which is valid for the entire program's lifetime.
String literals are 'static' :
(Structure Person { name: & 'static str, } fn main () { let p=Person { name: "fasterthanlime", }; }
[52, 49, 21] But owned strings are not static:
(Structure 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 ).
GIPHY App Key not set. Please check settings