# Rust

## Rust Book, 2nd ed.

Notes taken from the second edition of the Rust book.

### Common Programming Concepts

#### Variables and Mutability

• By default, all variables are immutable
• “Once a value is bound, you can’t change that value.”
• We can make a variable mutable by adding the keyword mut before the variable name
• This has some trade offs. e.g.: Large data structures may benefit from mutating the instance in place instead of recreating instances. For smaller data structures, the performance hit isn’t big enough to make it an issue so taking a more functional approach (e.g.: immutability & pure functions) would be desired
• Immutable variables are not quite the same as constants. Constants are always immutable, and are declared with the const keyword instead of let with the type annotation present
• Constants can also be declared in the global scope, unlike normal variables
• Constants must be set to a constant expression, not the result of any runtime operation (e.g.: the returned result from a function)
• Variables can be shadowed by declaring another variable with the same name. The newer variable’s value will be visible over the old variables.
• Useful for “transforming” a value while keeping the variable it belongs to immutable
• Since we’re actually re-creating the variable with each let, we can change the type in a later declaration. Allows us to avoid coming up with different names for single operations (e.g.: like space_str and space_num for a hypothetical space counting function)

#### Data Types

• Every value in Rust has a type
• Rust is a statically typed language, which means that the compiler must know the types of all variables at compile time.
• There are two subsets of types: scalar and compound
• Scalars represent a single value. There are four primary scalar types in Rust:
• Integers (8/16/32/64/arch-sized, signed/unsigned)
• Can write literals of certain numbers with a suffix to display its type. Can also have a visual separator (_)
• Nice safe default is i32
• Floating-point numbers (f32, f64)
• Nice safe default is f64, but it depends on the application
• Boolean (bool) (true, false)
• Character type (char)
• A Unicode Scalar value
• Compound types can group multiple values of other types into a single type
• Tupes: (1, 2)
• Used to group values with a variety of types
• Can access elements of a tuple by following the variable name by a period, followed by the index of the value we want
• Arrays
• Unlike tuples, all values must be of the same type
• Fixed-length; cannot grow or shrink in size
• Attempting to access an invalid array element will result in the program panicking and crashing (as opposed to C/C++ where we can illegally access out-of-program memory

#### How Functions Work

• main() is the entry point function for many Rust applications
• When is it not?
• Snake case is the conventional Rust style for function & variable names
• A Rust function:

fn my_cool_function() {
println!("Hey, I'm functioning over here!");
}
• A function call: my_cool_function();

• Order in which functions are defined does not matter; if FuncB is defined after FuncA, FuncA can still call FuncB

• Parameters must have their type declared:

fn another_cool_function(x: i32) {
println!("x is {}", x);
}
• Function bodies are made up of a series of statements and can optionally end with an expression. When an expression is the last line of a function body, it becomes that function’s return value

• Statements vs Expressions:

• Statements: instructions that perform an action, no return value
• This means some tricks available in other languages like C (e.g.: assignment operator chaining to assign a value to multiple variables at once) is not possible in Rust
• Expression: Evaluate to a resulting value
• The result of a macro is an expression
• Calling a function is an expression
• Block used to create new scopes is an expression
• Ending an expression with a semicolon ; makes it into a statement, meaning there is no return value
• Function declarations are considered statements

• Functions can return a value:

fn plus_five(x: i32) -> i32 {
x + 5
}

• Comments start with two slahes // and continue until the end of line

#### Control Flow

• if expressions

• No parens () needed around the condition, but expression body must be in a block {}
• Condition must be a bool - Rust won’t try to coerce non-boolean values to boolean
• else if for multiple conditions within an if expression
• else for all other branching paths
• if is can expression, so it can be used on the right side of a let:

let condition = true;
let number = if condition {
5
} else {
6
};

Remember that blocks evaluate to that last expression within them

• Rust has three loop types: loop, while and for

• loop will loop forever until told to stop

• Tell a loop to stop with a break keyword
• while [condition] {} will loop until the condition evaluates to false

• for is used to loopp over elements of a collection like an array or vector:

for element in array.iter() {
println!("{}", element);
}

for loops are considered safe and concise which makes them the most commonly used loop construct.

### Understanding Ownership

#### What is Ownership?

• A central feature to Rust is ownership
• All programs have to deal with computer memory management, usually done via:
• A garbage collector that looks for memory no longer used and frees it
• The developer explicitly allocate & frees memory
• (Rust): Ownership system sets rules as to what memory can be accessed when. No runtime overhead.
• Data where we know its size at compile time is added to the stack
• Fast to access data
• Data where we don’t know the size at compile time are created on the heap
• Slower to access data as we need to follow a pointer
• Ownership in Rust aims to make dealing with the heap easier
• There are some rules related to ownership:
• Each value in Rust has a variable called its owner
• There can only be one owner at a time
• When the owner goes out of scope, the value will be dropped
• Variables remain in scope from the point it was declared until the end of the current scope (block)
• String is a type that is allocated on the heap; therefore can store an amount of text unkown as compile time
• Memory is requested from the OS at runtime (done by the developer when they create a new string with String::new or String::from)
• We need a way of returning the memory when we’re done with String
• Rust does this when the variable that owns the value goes out of scope. It does this by calling a special function called drop
• When assigning stack values from one variable to another, a copy is made as the size of the type is known at compile time
• When assigning heap values (like String) from one value to another, that value is moved; the second variable now owns that value and the first variable loses ownership over the value
• Quote about double free errors (something Rust avoids with ownership):

This is a problem: when s2 and s1 go out of scope, they will both try to free the same memory. This is known as a double free error and is one of the memory safety bugs we mentioned previously.

• If we do want to copy the heap data, we can use a common method called clone

• We can use the Copy annotated trait on types made up of stack-values to create copies of them on assignment to another variable rather than a move

• Passing values to functions also takes ownership rules into account

• Returning values from a function can also transfer ownership

#### References and Borrowing

• References allow us to loan out many immutable (or a single mutable) versions of our value while leaving ownership with that value’s original variable (via pointers under-the-hood)
• This act is referred to as borrowing
• References are denoted with an ampersand & prefix in a type annotation (when defining arguments) or prefixed in front of the variable name (to actually create a reference to the value stored in that variable)
• References are immutable by default. There are strict rules when it comes to creating mutable referenes:
• Only one mutable reference to a value can exist in any given scope. Unlimited immutable references may be created
• An immutable and mutable reference to a value cannot exist at the same time within any given scope
• Remember that curly braces {} can be used to create a new scope
• We make a reference mutable by adding the mut keyword after the ampersand & (e.g.: &mut foo, foo: &mut i32)
• Dangling pointers (a pointer that references a memory location that may have been given to someone else) are impossible to have in (safe) Rust

#### Slices

• Slice is another data type that has no ownership. It’s a reference to contiguous sequence of elements in a collection (instead of the entire collection)
• A string slice is a reference “to an internal position in the String […]”
• Internally, the string slice stores the starting position & the length of the slice
• Byte literal syntax: b' ' (for a space character)
• String slices look like so:

let s = String::from("hello world");

let hello = &s[0..5];
let world = &s[6..11];

The format of the range is starting_index..ending_index. Lower bound inclusive, upper bound exclusive.

• Starting from 0 requires no starting_index value: ..2

• Ending with the last byte of a String means we can drop the ending_index: 2..

• Drop both starting_index and ending_index to take a slice of the whole string

• The type for a string slice is &str

• A string literal is a string slice

• Using a string slice &str as a function argument means that argument can accept both &str and String

• Arrays of other types can also be sliced

### Structs

#### General

• Defined with keyword struct [name]
• Can be defined with no data
• Create new structs with let my_struct = [name] { field: value, ... }
• Access struct fields with dot notation
• Use String rather than &str since the struct should own its data
• A struct can store a reference to data owned by something else but it requires the use of lifetimes
• Printing out the contents of a variable with println! works by using a type’s Display trait - new structs don’t have this implemented by default (how would Rust know how the user wants to display the struct?)
• There are useful derived traits that can be easily applied to structs to add additional functionality. Use a derived trait by annotating the following above a struct: #[derive(TRAIT, ..)]
• Debug is one derived trait for using the {:?} format string type to see a struct’s values
• {:#?} is a “pretty debug log” style, easier to read for larger structs

#### Method Syntax

• Similar to functions except defined within the context of a struct (or enum or trait object); the first parameter, self, always represents the struct the method is being called on
• Methods are defined with an impl block. Example:

struct Bob;

impl Bob {
fn name(&self) -> String {
String::from("I am the contrived example!")
}
}

fn main() {
let bob = Bob;
let name = bob.name();
}
• Methods can take ownership of the object it is called on, borrow immutably or mutably

• Unlike C++, there is no need for a dereferencing operator -> as Rust performs auto-referencing & auto-dereferencing

• Associated functions (similar to “static” methods or class methods) are defined within a struct’s impl block but do not take self as the first argument. They are called by using the struct’s name followed by double colons followed by the associated function’s name (e.g.: String::from)

### Enums and Pattern Matching

#### Defining an Enum

• “Rust’s enums are most similar to algebraic data types in functional languages like F#, OCaml, and Haskell.”
• Enums enumerate all possible values of a certain domain/area. A variable that holds an enum value can be one of the values on the enum but not multiple at the same time
• Enums variants are namespaced under the enum name, use double colon to access (e.g.: ~MyEnum::Five)
• An enum variant can be associated with some data, meaning no extras struct are needed to associate data with an enum
• Each variant can have different types & amounts of associated data
• Enums are useful as a function/method argument could take an enum, meaning any variant of that enum would be a valid value
• Enums can have methods on enums with impl blocks
• Rust doesn’t have the concept of null vales; instead, it makes use of an enum in the standard library called Option, which is used to represent a possible value or a lack or value
• Variants of Option are Some(T) and None
• Option(T) and T are different types; Option(T) must be converted to T before being able to use it. This means we never use a non-null value when it’s actually null
• match expression is often used to unwrap Option(T to T / None

#### The match Control Flow Operator

• Match patterns can be made up of literals, variable names, wildcards and others
• Each variant of an enum in a match statement needs to have an arm that handles that variant
• Syntax:

enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}

fn value_in_cents(coin: Coin) -> i32 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
• Return type of match can be of any type, as long as each arm returns the same type

• The code in an arm is an expression and therefore gets returned as the result for the entire match if the pattern matches

• We can extract values out of enum variants by adding the value to the pattern in match:

fn value_in_cents(coin: Coin) -> i32 {
match coin {
Coin::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
},
_ => 1,
}
}
• We can match against Option<T> to get either Some or None

• When we don’t care about all the potential arms/possibilities, we can use _ as a way to say “everything else that doesn’t match gets handled here” - it matches any value

#### Concise Control Flow with if let

• Match statements are a bit much to use when you only care about matching a single pattern and ignoring everything else
• if let allows us to handle values that match only one pattern:

if let Some(3) = some_u8_value {
println!("three");
}

if let takes a pattern and expression separated by =

• We lose the exhaustive checking of match statements in exchange for less boilerplate/typing/cleaner code

• if let can include an else, which is the same as a _ arm in a match

### Modules

#### mod and the Filesystem

• Module definition starts with the mod keyword, followed by the name of the module, followed by a code block containing the module
• Calling a function inside a module is done with double colon syntax: module::function()
• Modules can contain other modules
• Modules end up forming a hierarchy with the top module at the highest level and submodules lower down
• To move a module into its own file, just copy the contents of the module into its own file. Refer to that module by using mod [modulename]; The file should have the same name as the module so the Rust compiler can find it
• By default, Rust follows modules linked from the main source file - if a module isn’t declared with mod, it isn’t compiled. Just having a module in a source file isn’t enough to compile it.
• When modules get complicated and end up holding submodules, move the modules to their own folder with the name of the parent module of that group, and name the “parent module” file mod.rs. This is needed so Rust can figure out the module hierarchy; if two module files were on the same “level”, how would Rust know one was a submodule of another?
• Declare submodules in the parent module with the mod keyword

#### Controlling Visibility with pub

• The top-level module of a crate is known as the root module
• By default, items defined in a module are private to everything outside the module and public to the module it is declared in & child modules
• Private functions will give off a dead_code warning; public onese wouldn’t as they are meant for outside consumption
• Making something public in Rust requires adding the pub keyword to the start of a declaration
• Modules, structs, types, and functions all need to be marked pub in order to be used outside of the crate

#### Importing Names

• Fully qualified names of functions inside modules can be long to type out. We use the use keyword to bring items from modules (or modules themselves) into scope of the current module.
• e.g.: use path::to::module::to::use
• This only brings the specified module into scope; child modules are not automatically brought into scope as well
• Enum variants can also be brought into scope with use
• use paths are relative to the crate root
• All items from a module/namespace can be imported at once with the glob * operator. However, this is discouraged as one may accidently pull in items with conflicting names
• Use super in the fully qualified name to access a parent module
• Starting a path with double colons :: means to start from the crate root
• Starting a path with keyword super means to start from the module one up the hierarchy from the current one

### Common Collections

#### General Notes

• Unlike built-in array and tuple types, data belonging to collections is stored on the heap; amount of data stored can grow/shrink
• Three collections that are often used in Rust:
• Vector: store variable number of values next to each other
• String: a collection of characters
• Hash map: associates a value with a particular key

#### Vectors

• Type: Vec<T>
• Can only store values of the same type
• Create a new vector:
• Vec::new(): requires type annotation on variable
• vec![] macro: Rust can infer type if values are initially given
• Add new elements to the end of the vector with vec.push(item)
• The target vector must be marked as mut
• When a vector goes out of scope (& is dropped), all of its elements are dropped as well
• Access elements of a vector with either index syntax (e.g.: &vec[0] to get a reference to that element) or with vec.get(0) to return an Option<&T>
• Trying to access an out-of-bounds index will result in a panic! in the first case, but a None with no panic in the second case. Which to choose depends on how you want your program to react in this case
• Borrow checker will enforce ownership/borrowing rules on references from vectors
• One can get around vectors only storing one type with enums, and having different enum variants store data (e.g.: variant Int(i32), variant Float(f64))

#### Strings

• Strings are a collection of bytes in Rust
• Rust has one string type in the core language: the string slice str
• String is “a growable, mutable, owned, UTF-8 encoded string type”
• Create a new string with String::new()
• Convert a slice or string literal to a String with the slice method to_string
• String::from(slice) also creates a String from a string literal
• Update a string with the overloaded addition + operator which will concatenate strings. One can also push_str onto it
• Keep in mind that + takes ownership of the left hand value
• Use format!() macro for concatenating multiple strings as it returns a String and does not take ownership of any params
• push(char) also works on Strings
• Rust does not allow for using index syntax to access individual characters in a string
• This is because UTF-8 characters can be larger than a single byte and String itself is a wrapper over Vec<u8>
• This makes string slicing potentially dangerous as well, as Rust will panic at runtime if trying to access a range that falls between character boundaries
• Calling .chars() on a string type returns an iterator that can be used to iterate over characters
• .bytes() returns each raw byte in a string
• Strings are hard

#### Hash Maps

• HashMap<K, V> “stores a mapping of keys of type K to values of type V
• HashMap needs to be brought into scope with use std::collections::HashMap;
• Create a new HashMap with new, add elements to an instance with insert(k, v)
• Another way to create a hash map is by calling collect on a vector of tuples, where each tuple has 2 elements: 0 being the key, 1 being the value
• Requires the variable being assigned to to have a type annotation of HashMap<_, _> since collect can return to different data types
• Hash maps own their values
• We can store references in hash maps but this requires the use of lifetimes
• Get a value from a hash map by providing a key to its get method
• Remember that get returns Option<V>, not the raw value
• Iterate over hash maps with the keys being a tuple containing the key and value: (key, value)
• insert will overwrite any value that key currently has if a value exists
• Only insert if key has no value: entry can be used to check whether a value exists; it returns a variant of the Entry enum. or_insert(value) can be called on this Entry enum that will insert that value if the entry doesn’t exist
• or_insert returns a mutable reference to the value for this key! That reference must be dereferenced with * before using

### Error Handling

#### Unrecoverable Errors with panic!

• The panic! macro takes a failure message and will unwind the stack, clean it up and then quit

• Can set the following in Cargo.toml to have panic! just abort instead of unwinding the stack & cleaning it up:

[profile.release]
panic = 'abort'
• Example: panic!("here is my message!");

• Set the RUST_BACKTRACE environmental variable to see a backtrace on panic!

• How to read backtraces causes from library panic!: start from the top until you get to the file you wrote

#### Recoverable Errors with Result

• Most of the time, an error doesn’t require stopping the program entirely
• We can interpret & respond to errors instead
• The Result enum has two variants:
• Ok(T)
• Err(E)
• We can use Result to deal with operation errors gracefully
• Example: File::open("my_file.txt") returns a Result<std::fs::File, std::io::Error>
• We can use match expressions to unwrap a Result and either return the data associated with a successful operation, or receieve an error message and deal with it appropriately
• Result (and Option) are imported in the prelulde so no need to import them or bring them into scope
• Prelude is list of things that automatically gets imported into all Rust programs.
• We can use a match expression inside the Err arm of a match expression matching against a Result to match against specific errors. For example, if we try to open a file and the error.kind() is ErrorKind::NotFound, we could create the file and then continue instead of just panicking
• We could have an if expression inside a match arm condition. This is called a match guard and is just an additional conditional used. (e.g.: Err(ref error) if error.kind() == ErrorKind::NotFound => { ...
• Why ref instead of &? & matches a reference and gives us a value, ref matches a value and gives us a reference to it
• Shortcuts for this match on Result pattern (return value if exists, panic if not) exists via the Result::unwrap and Result::expect methods
• unwrap has arity 0, expect takes an error message in a string slice
• A function can propagate errors to its caller by having a return type of Result<T, E>
• Within a functin that has a return type Result<T, E>, instead of using match, unwrap or expect on methods within this function that themselves return Result, we can add a question mark operator ? (called the try operator) that is essentially syntactic sugar for the match - if good return value, if bad return error pattern
• e.g.: let mut f = File::open("hello.txt")?;

#### To panic! or Not To panic!

• There is no way to recover from a panic!. Result gives the caller options on how to deal with a potential error
• When defining a function that may fail, returning Result is a good default choice
• panic! calls are appropriate in tesst (where a panic causes a test to marked as a fail), examples and prototypes (where conciseness is more appropriate than having lots of boilerplate code)
• Sometimes, if we know 100% that calling unwrap won’t result in a panic, it’s appropriate (e.g.: parsing a hardcoded IP)
• panic! when it’s possible you can end up in a bad state (e.g.: in a game, if a window handle cannot be created - no point in continuing)
• If a bad state is reached but it is expected, returning a Result is more appropriate

### Generic Types, Traits and Lifetimes

#### General

• “Generics are abstract stand-ins for concetre types or other properties”
• A way to “deal effectively with duplication of concepts”.
• A technique for dealing with duplication is to extract that code out into its own function

#### Generic Data Types

• We use generics where we usually place types in function signatures or structs. This allows us to use this function/struct for various concrete data types
• In functions, generics are defined in the signature
• We need a name for the generic type parameter: T is a good default
• First, the type name needs to be declared in angle brackets <> between the name of the function & the parameter list. This is so when we use the type parameter in the signature of the function, the compiler doesn’t complain that the type hasn’t been declared

fn largest<T>(...) { ... }
• Then, we can use the type parameter in the function signature:

fn largest<T>(list: &[T]) -> T { ... }

This reads “largest is a generic over type T. It has one parameter named list, which is a slice of values of T. A T is returned.

• In structs, generic type parameters are declared in a similar way. Declare the type parameter after the struct name, then just substitute the type parameter wherever you want generic types in your struct

• Generic type parameters are declared in enums the same way they are in structs

• All T provided to a function or struct need to be of the same concrete type

• When defining generics on struct/enum methods one must declare the type parameter (.e.g: T) right after the impl keywprd:

impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
• Rust code with generics is not any slower than using concrete types. This is because at compile time, Rust creates specific code with concrete types from the generic code in a process called monomorphization.

• Rust will look through the code and replace any areas with generics with concrete versions of that enum/struct.
• tl;dr: No runtime cost for generics!

#### Traits: Defining Shared Behavior

• Similar to interfaces in other languages but not 100% the same
• Traits allow us to abstract behaviour that may be shared between types
• Trait bounds are used to specify, at compile time, that a particular type has a particular trait and therefore, we are guarantees that said type contains certain behaviour
• Behaviour in this context means methods we can call on that type
• Declare a trait with the trait keyword, followed by trait name, followed by a block containing method signatures

pub trait Summarizable {
fn summary(&self) -> String;
}

Any type that implements the Summarizable trait is guaranteed to have the summary method which returns a String. Therefore, if we have a method that takes a type that implements Summarizable, we can know with certainty that we can call that method on any value passed in

• A method definition that ends with empty curly braces {} instead of a semicolon ; means that method is optional.

• Implementing traits on types looks similar to an impl block for implementing struct methods, except we specify a trait we are implementing and a type that said trait is being implemented onto:

impl MyTrait for MyType {
fn my_method(&self) -> MyType {
...
}
}
• Traits need to be brought into scope with use in order to use any methods defined by a trait

• We cannot implement external traits on external types: either the type or trait needs to be local to our crate

• Earlier, I said that some methods defined in a trait can be marked as “optional” by ending them with empty square braces {} instead of a semicolon ;. A default implementation can be provided within the square brackets instead of leaving it empty. This means that the developer implementing the trait doesn’t need to implement that method as it already has some default functionality

• Traits can be mixed with generic type parameters to constrain them to a certain subset of types instead of just “all types”. This is called specifying trait bounds on a generic type. Follow the generic type parameter by a colon and the trait you wish to bound that generic with:

pub fn notify<T: Summarizable>(item: T) {
println!("Breaking news! {}", item.summary());
}

In the above example, the function notify takes a type T bound to the trait Summarizable. We know that T must implement this type so we can call any of that trait’s methods.

• Multiple trait bounds can be specified on a generic with the plus + operator. This would mean the generic type would need to implement all traits bound on it

• When using multiple generic type parameters and trait bounds, moving the trait bounds to a where clause after the function signature:

fn some_function<T, U>(t: T, u: U) -> i32
where T: Display + Clone,
U: Clone + Debug
{ ... }

• Every reference in Rust has a lifetime which is the scope for which that reference is valid
• Most lifetimes are implicit and inferred like types
• Can be considered “Rust’s most distinctive feature”
• The main goal of lifetimes is to prevent dangling references: a reference to data other than the data we intend to reference.
• Uninitialized variables cannot be used
• The borrow checker is the part of the compiler that checks whether all borrows are valid. Essentially, data needs to live longer than their references.
• Lifetime annotations don’t change how long a reference has to live but relate the lifetimes of multiple references to each other
• Names of lifetimes start with an apostraphe '. Names themselves are all lowercase and usually short. 'a is a good default
• Lifetime annotations go after the ampersand & of a reference, followed by a space, followed by the reference type:

&i32; // reference
&'a i32; // reference with explicit lifetime
&'a mut i32; // mutable reference with an explicit lifetime
• A function with the following signature: (first: &'a i32, second: &'a i32) means that references first and second “must both life as long as the generic lifetime”

• Like generic type parameters, lifetime parameters must be defined within angle brackets <> between the function name and param. list:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}

This signature says that for some lifetime 'a, the function gets two arguments that will live at least as long as 'a. The string slice returned by the function also will last as long as 'a.

• This does not change the lifetimes but says that any values that do not adhere to this contract should be rejected by the borrow checker
• Returning a reference from a function requires the lifetime parameter for the return type to match the lifetime parameter of one of the arguments.

• If the reference returned does not refer to one of the arguments, then the value being referred to must have been created from the function in question. This would also be a dangling reference as the value is dropped as it’s scope is the function it was defined in.
• Remember that lifetimes are only useful relative to other lifetimes for other references, not by themselves
• Structs can hold references but it requires lifetime annotations on each reference in the struct definition:

struct ImportantExcerpt<'a> {
part: &'a str,
}

fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.')
.next()
.expect("Could not find a '.'");
let i = ImportantExcerpt { part: first_sentence };
}

In the above example, part is a field that holds a reference to a string slice. i and novel must live for the same length of time.

• As Rust matures, the compiler may grow in ways that requires fewer lifetime annotations due to the borrow checker getting smarter. The patterns programmed into Rust’s reference analysis are called lifetime elision rules

• Lifetimes on function/method parameters are calld input lifetimes

• Lifetimes on return values are called output lifetimes

• Each parameter that is a reference gets its own lifetime parameter.
• If there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters
• If there are multiple input lilfetime parameters but one of them is &self~/~&mut self (because it’s a method) then a lifetime of self is assigned to all output lifetime parameters
• We need to explicitly annotate lifetimes when these rules are applied and there are still parameters that are references without lifetimes
• For methods we need to declare the lifetime in the angle brackets <> after the impl keyword (just like generics parameters) and also after the struct’s name:

impl<'a> ImportantExcerpt<'a> { ... }
• There is a special lifetime known as 'static that is in scope for the entire duration of the program running. All string literals have this lifetime. These values are stored directly in our application’s binary.

• Only use as a last resort
• Generic type parameters, trait bounds and lifetimes can be all used at once!

### Testing

#### How to Write Tests

• A test in Rust is a function annotated with the test attribute.
• An attribute is metadata about code: derive is another one we’ve been using. Appear on the line above the code we wish to annotate
• Annotation for tests looks like this: #[test]
• Run tests with cargo test
• Doc tests are code examples that appear in our API documentation - this can be tested too! (Very cool!)
• Tests fail when something in the test function panics
• tests is a module; to test code defined elsewhere in the crate, we need to import it. Easily done with use super::*
• assert! is a macro that takes an expression, evaluates it, and will throw a panic! if that expression evaluates to false
• assert_eq! and assert_ne! take two arguments and panic if those arguments are not equal or equal, respectively
• Requires objects being compared to have both the PartialEq and Debug traits to be able to compare themselves to other values of the same struct/enum, and to be able to output errors to stdout
• The assert macros take an additional argument after the required ones that gets passed to format! and acts as the error message
• Another attribute exists, should_panic that makes the test pass if the test panics. The test fails if no panic occurs.
• The should_panic attribute goes after the test attribute
• Takes an additional expected argument that will check for a specific panic

#### Controlling How Tests are Fun

• By default, cargo test runs all tests in parallel
• To pass options to the binary: cargo test -- --help. After cargo test, add two hyphens (–) as a separator and then add your options
• The test binary accepts --test-threads=NUM to specify how many threads to run tests with (1 = no parallelism)
• By default, test binaries do not output the result of println if a test passes. Pass the test binary the --nocapture flag to see printed values for passing tests.
• To run a single test, pass the test runner the names of the tests you wish to run. You can specify part of a test name, and any name that matches that value (even a partial value) will run
• Use the ignore attribute to ignore a test. Run ignored tests by passing the option ignored to the test binary

#### Test Organization

• Unit tests test each unit of code in isolation. Unit tests go in /src in each file with the code it is testing. Creating a submodule in each file to contain the test functins. Annotate the module with cfg(test)
• cfg(test) tells Rust only to compile & run that code when cargo test is executed
• Private functions can be tested (as the tests module would be a submodule of the module where said private function was defined)
• Integration tests call the public API functions. Purpose is to test that parts of the library correctly work together
• Integration tests live in the top-level tests directory of a project
• Will need to import our own crate with extern crate CRATENAME. Each test in the tests directory is an entirely separate crate
• cfg(test) is not needed since the entire directory is treated in a special way
• To run a particular integration test, pass the --test argument to the test runner followed by the name of the file
• Integration tests may contain submodules. The files in subdirectories of /tests do not get compiled, so this is an ideal place to put any shared test functions
• Only library crates can create & use integration tests
• This is why many projects have their src/main.rs simply call into functions defined in src/lib.rs: to be able to write integration tests

### An I/O Project Building a Small Grep

#### General

• grep: “*G*lobally search a *R*egular *E*expression and *P*rint”
• Grep takes the following steps to search a file:
1. Take as arguments filename & string
3. Find lines in file that contain string argument
4. Print out those lines

#### Accepting Command Line Arguments

• We will run our command like so: cargo run [searchstring] [filename]
• std::env::args is a function returning an iterator of the arguments given to the program
• Will crash if string contains invalid Unicode; use std::env::args_os for that
• When collect() is called on an iterator, it creates a collection of the type secified in the variable’s type annotation. collect requires type annotations as it can’t infer the type of collection we want
• In typical fashion, the first argument of args is the binary name

• To read a file, we need the following use statements:
• std::fs::File to deal wtih files
• std::io::prelude::* contains various traits useful for doing IO
• We get a mutable handle to a file with File::open(path)
• file.read_to_string(&mut string) can be used to read the contents of a file into a string (who woulda thought with a name like that!?)

#### Refactoring to Improve Modularity and Error Handling

• main is too big. The Rust community has developed a guideline process for splitting up separate concerns in binary apps when main gets too big:
1. Split program into both lib.rs and main.rs, move logic into lib.rs
2. While parsing logic is small, keep it in main.rs
3. When parsing logic gets too big, extract it into lib.rs as well
4. Responsibilites of main after this:
• Calling CLI parser logic with argument values
• Setting up other config
• Calling a run function in lib.rs
• If run returns an error, handling that error
• Hard to tell the differene between “configuration variables” (query and filename) vs normal variables
• Instead of returning a tuple that gets immediately destructured (which is pointless), this could be a sign that a struct (grouping the values logically) could be the solution
• Our code uses clone to turn stirng slices into owned strings. Be careful with clone, it’s not efficient and it needs to copy heap memory. However, for new Rust users, it can be useful to do insetad of fighting the borrow checker
• “File not found” is not an accurate error message for the operation being performed; there could be different reasons for failure including lack of permissions to access said file
• expect will cause a panic if too few CLI arguments are passed in; this is not ideal
• Instead of panicking which throws other additional info from Rust, handle the error gracefully by returning a Result and dealing with that
• A nice way to deal with that stuff is unwrap_or_else which takes a closure (passed an err message where we can handle what to do if things go wrong
• std::process::exit(err_code) is a nicer way to quit of the program than panicking
• We can have functions return a Result<(), Box<Error>> where if successful, the function returns a type that implements the Error trait
• Splitting a binary crate to have a library component is easy. Just copy all the library specific stuff into src/lib.rs, and in src/main.rs import both the crate & whatever is needed from the crate.

#### Testing the Library’s Functionality

• Now that we’ve extracted logic from main to the library, we can properly test all of our business logic
• Test Driven Development: Write a test that fails since the implementing code isn’t written yet, write enough cost to make test pass, refactor & repeat

#### Working with Environment Variable

• Functions for working with env variables can be found in std::env: in particular, std::env::var. This returns a Result.
• is_err is a method on Result that will return true is an error was returned, false otherwise. This is so we can act on the env. variable not being set in a way that doesn’t result in a panic -

#### Write to stderr Instead of stdout

• Differentiating between stderr and stdout is nice for end-users who expect this sort of behaviour
• println! outputs to stdout by default
• Writing to stderr needs the io::prelude to be imported
• Create a handle to stderr with std::io::stderr()
• Pass that handle (a mutable reference) as the first argument to writeln! which is a rawer version of println! that allows us to say where to output the text to

### Functional Language features in Rust

#### Closures: Anonymous Functions that can Capture their Environment

• A closure is an anonymous function
• Can create the closure in one place, & call it to evaluate under a different context
• Closures capture variables from the scope in which they are called
• Some function calls can be expensive & slow (& blocking)
• (Long convoluted example in this chapter, IMO)
• Closures are defined liked so:

// example of assigning a closure to a variable
let my_closure = |num| {
println!("Hi, your number is {}", num);
num
}

A closure consists of a pair of vertical pipes containing any arguments, followed by a block containing the closure’s body

• Call a closure assigned to a variable just like one would call a normal function

• One difference between closures and functions is that closures don’t require type annotations on parameters or return values (although they can be added if the developer wishes)

• If we let Rust infer the type, we can’t call the closure twice successively with different types: the first type the closure is called with will be the ‘inferred type’ that sticks with that closure
• If we want a struct to hold a closure, we need to specify a generic type parameter:

struct Cacher<T>
where T: Fn(i32) -> i32
{
calculation: T,
value: Option<i32>,
}

The type parameter T stored in the field calculation takes a closure that takes an i32 as an argument and returns an i32

• Closures have access to variables from the scope in which they’re defined

• Closures use extra memory to store the environment variables for use
• The three ways a closure can capture values is reflected in the three Fn trait types:

• FnOnce takes ownership of variables it captures, moves them into closure. FnOnce cannot be called more than once in a context
• Fn borrows values immutably
• FnMut borrows valuels mutably
• If we want a closure to forcefully take ownership of values it uses in the environment, use the move keyword before the parameter list

• Useful for passing closure to a new thread

#### Processing a Series of Items with Iterators

• An iterator handles logic for iterating over items in a sequence and figuring out when to stop (e.g.: the end of the sequence has been reached)
• Rust iterators are lazily evaluated
• Creating a iterator doesn’t do anything; using the iterator does
• Iterators can be used in for loops
• Iterators all implement the Iterator trait which consists of the type of the collection (as an associated types) as well as a method named next that either returns a value or doesn’t (signaling we’ve reached the end of the sequence)
• The iter method creates an iterator over immutable references
• into_iter exists to take ownership
• iter_mut exists to borrow mutably from the collection
• Iterator trait has a number of methods with default implementations that call next known as consuming adaptors since calling them uses up the iterator
• e.g.: sum
• Some methods in Iterator produce other iterators; these are known as iterator adaptors and can change one iterator into another
• e.g.: map takes a closure that will be called on each item which produces a new iterator
• map doesn’t actually do anything (consume the iterator) by itself, we have to use collect for that
• filter takes a closure that performs a test on each value and returns a boolean
• If the boolean is true that value will be included in the new iterator

#### Improving our I/O Project

• Instead of cloning some String object in our Config object’s constructor, we can pass the env::args() iterator directly to our Config constructor and just iterate over the values there
• The search function could have its iteration section refactored as follows:

fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
contents.lines()
.filter(|line| line.contains(query))
.collect()
}

We use filter to get only the lines we cared about and then collect the results into the type specified in the function’s retrun value

#### Comparing Performance: Loops versus Iterators

• Iterators are slightly fater than for in Rust
• They get compiled down to roughly the same code as if we wrote it ourselves
• Considered a “zero-cost abstraction”

### More about Cargo and Crates.io

#### Release Profiles

• A release profile is a a configuration of various options that tell Cargo how to compile our project
• There are four release profiles supported by Cargo:
1. dev: used for cargo build
2. relase: used for cargo build --release
3. test: used for cargo test
4. doc: used for cargo doc
• Customize options in Cargo.toml with a section named [profile.NAME]

• e.g.: default dev and release profiles:

[profile.dev]
opt-level = 0

[profile.release]
opt-level = 3
• opt-level controls how many optimizations Rust will apply

• Valid values is [0, 3]

#### Publishing a Crate to Crates.io

• Documentation comments /// can be used to generate HTML documentation for our project
• Documentation is only generated for library crates
• Markdown is supported
• Documentation comments go right before the item they are documenting
• cargo doc is used to generate the documentation
• Adding examples in the documentation comments can be useful as cargo test will actually execute that code as part of our tests
• Another style of doc comment //! is used to comment containing items (crates, modules or functions) instead of items following it
• Typically used in crate root or a module root to document the crate/module as a whole
• pub use can be used to re-export items to have the public-facing structure of our library appear different (and hopefully more developer-friendly)
• e.g.: Re-exporting enums or functions from the top-level of the crate rather from within their respective submodule - does the consuming developer care that function A is in submodule B? Probably not
• Before publishing to crates.io, one needs to register an account there
• Publish a crate with cargo publish
• Publish a new version by changing the version number and then cargo publish

#### Cargo Workspaces

• Workspaces is a feature of Cargo that helps a developer manage multiple packages developed in tandem
• Workspaces share the same Cargo.lock and output directory
• Tell Cargo that a package is a workspace by adding [workspace] to the bottom of Cargo.toml
• Any crates our main package depends on as sub-directories are a part of the workspace
• Simply run cargo run from our project root to easily bootstrap a new sub-package
• Adding a new dependency to a sub-package’s Cargo.toml will add it to the top-level Cargo.lock - however, the crate will be unavailable to any packages that don’t have that dependency listed in their Cargo.toml
• In a workspace project, a top level run of cargo test will only run the top-level packages’s test. Specify a package to run tests for with cargo test -p PACKAGE-NAME
• Each workspace gets published to crates.io separately

#### Installing Binaries from Crates.io with cargo install

• This is a way f or Rust developers to install tools form crates.io
• Must be a binary crate in order to be installed
• With rustup, all binaries are installed to $HOME/.cargo/bin - add this to your path! #### Extending Cargo with Custom Commands • “If a binary in your $PATH is named cargo-something, you can run it as if it were a Cargo subcommand by running cargo something.”
• List all commands with cargo --list

### Smart Pointers

#### General

• As a refresher (been a while since I did C/C++), a pointer is a term for something that refers to a location that stores sometihng else
• A reference in Rust is a form of a pointer
• Smart pointers act like pointers but canc ontain additional metadata and functionality (e.g.: reference counting)
• In a way, String and Vec<T> are both smart pointers; own some memory and allows us to manipulate it, while providing additional metadata like string capacity or additional functionality (guarantees that the string is always UTF-8)
• Smart pointers implement the Deref and Drop traits

#### Box<T> Points to Data on the Heap and Has a Known Size

• A box is the most straight-forward of all smart pointers in Rust
• Allows us to put a single value on the heap
• e.g.: storing an i32 on the heap:

fn main() {
let b = Box::new(5);
println!("b = {}", b);
}
• When a box goesout of scope, it will be deallocated for both the box (itself stored on the stack) and the data it points to (on the heap)

• Boxes are useful for when a struct/enum has a field that is of the same type; this could lead to Rust complaining that this could happen an infinite amount of times and it does’t know the size of this structure, refusing to compile

• A Box always takes up usize amount of space, which is the address of the heap data. Using a Box in this situation would appease the Rust compiler, as the size of this structure would be known as compile time

This is the main area where boxes are useful: breaking up an infinite data structure so that the compiler can know what size it is.

#### The Deref Trait Allows Access to the Data Through a Reference

• Deref allows us to override the dereference operator *
• When we have a variable that holds a mutable reference to a value and we want to access it to change it, we need to use the dereference * operator in front of the reference variable name in order to access the actual value the reference points to and not just the reference itself
• When dereferencing a smart pointer which contains meta data, we only want to access the main data & to be able to use smart pointers wherever we use regular references - this is what the Deref trait allows for
• Example of implementing the trait object for a Mp3 struct which holds its “main” data in a Vec<u8>:

impl Deref for Mp3 {
type Target = Vec<u8>;

fn deref(&self) -> &Vec<u8> {
&self.audio
}
}

fn main() {
let my_favorite_song = Mp3 {
// we would read the actual audio data from an mp3 file
audio: vec![1, 2, 3],
artist: Some(String::from("Nirvana")),
title: Some(String::from("Smells Like Teen Spirit")),
};

assert_eq!(vec![1, 2, 3], *my_favorite_song);
}

Implementing the Deref trait on Mp3 allows us access the main data associated with the struct by using the dereference * operator

• Without Deref, the comiler can only dereference & references

• The deref() method returns a reference to the data we want, which can then be immediately dereferneced

• The reason a reference is returned that we dereference instead of just returing the value is ownership; we don’t want to move the value out of its struct / original owner
• Deref coercion of arguments often happens implicitly, which is unusual for Rust

• A deref coercion automatically converts a reference to any pointer into a reference to that pointer’s contents
• This happens when the reference type of the argument differs from the reference tye defined in the parameter
• This allows us to treat smart pointers as if they were regular references
• There is also a DerefMut trait to get a mutable reference from dereferencing

#### The Drop Trait Runs Code on Cleanup

• Drop allows us to run some code when its value is going out of scope
• Can be used to deallocate memory or decrement a reference counter
• Can be used to manage resources beyond memory (e.g.: drop a network connection)
• The Drop trait requires the implementation of one method, drop, that takes a mutable reference to self
• drop method gets called automatically as smart pointer is going out of scope
• Can use std::mem::drop to drop a value earlier than when its containing scope ends if extra control is needed
• This function has an empty body but it takes ownership of whatever value passed to it, which immediately goes out of scope and is dropped
• (I kinda love this; the simplicity of how this works & taking advantage of the language rules)

#### Rc<T>, the Reference Counted Smart Pointer

• What if a value needs to be owned by multiple “owners”? We use the Rc<T> or reference counting smart pointer
• Reference counting means it keeps track of the number of references in order to know if the value is still in use
• When the reference counter reaches 0, we know the value is safe to cleanup without creating invalid references or “dangling pointers”
• Rc<T> is for when we want to allocate heap data for multiple parts of our program yet we cannot determine at compile time which parts of our program will finish using it last
• Only for use in single-threaded scenarios
• Rc is not in the prelude so we need to use std::rc::Rc;
• The method of creating a new reference from an Rc is to call the clone method on it
• Rc<T> holds the reference count, clone increases that counter
• We can use the Rc::strong_count(rc) function to get the reference count of an Rc
• When a reference goes out of scope, the reference count is decreased
• All references created with Rc are read-only; else we’d run into data race conditions

#### RefCell<T> and the Interior Mutability Pattern

• Interior mutability means we are allowed to mutate data event hrough there are immutable references to that data
• This pattern involves unsafe code wrapped in a safe API
• The type that allows this in Rust is RefCell<T>
• Can only have a single owner
• Only for use in single-threaded scenarios
• RefCell allows us to enforce borrowing rules at runtime instead of compile time
• Breaking the rules will cause a panic!
• It is difficult for Rust to be 100% sure about the rules it is trying to enforce; for times when the developer 100% knows better, use RefCell
• Use borrow and borrow_mut methods to create references and immutable references
• borrow returns smart ponter type Ref
• borrow_mut returns smart ponter type RefMut
• Both these types implement Deref so we can treat them like regular references
• RefCell is not in the prelude so it needs to be use std::cell:RefCell;
• Remember that we don’t get around borrowing rules with RefCell; we just defer those checks until runtime
• This means using RefCell brings a performance penalty
• Rc<T> and RefCell<T> can be combined to give us a type that has reference counting and is mutable
• Rc<T> only allows us to have an immutable reference to T
• RefCell<T> is an immutable reference with interior mutability
• Cell<T> is similar but instead of giving references to the inner value, the value is copied in and out of the Cell<T>
• Mutex<T> allows for interior mutability that is safe across threads

#### Creating Reference Cycles and Leaking Memory is Safe

• A memory leak is memory that is allocated yet never cleaned up after
• Memory leaks in Rust are not impossible, just hard to do
• In Rust, memory leaks are memory safe
• Rc<T> and RefCell<T> can be used to create a cycle of references where items refer to each other
• This is bad; the ref. counter for both items will never reach 0
• For large programs that allocate lots of memory an dhold onto it for a long time, this can end up being an issue
• Some data structures such as graphs, cycles are needed when parent nodes point to children and children point back at their parents
• If ownership is expressed only in one direction, a possible solution would be using Weak<T>
• Weak<T> is a smart pointer for when the situation has cycles of references but ownership only be expressed in one direction
• We want to show relation but not ownership; that is what Weak<T> is for
• It’s another way of referencing an Rc<T> that doesn’t increase the strong count
• Weak<T> is a way to reference an Rc<T> that does not increase the strong count, but the weak count
• Rc will go out of scope no matter what the value of the weak count is
• Get a value from a Weak<T> by calling upgrade on it, turning it into an Option<Rc<T>>
• If the value has not dropped, the result will be Some, otherwise None
• Because upgrade returns an Option, we can be sure that our weak references going out of scope will be handled
• If a strut had a parent field, that should be of type RefCell<Weak<T>>
• Create a Weak reference from an Rc with Rc::downgrade(&rc)

### Fearless Concurrency

#### General

• Another major goal of Rust is concurrent programming, where different parts of a program run at the same time
• Ownership rules and type checking can catch maany concurrency-related errors at compile time rather than runtime

#### Using Threads to Run Code Simultaneously

• A program executes in a operating system context called a process
• The OS managing processes is what lets multiple programs run at the same time
• Our programs can have multiple parts that run simultaneously within the context of itself: this functionality is enabled by something called threads
• When a program has its work split across different threads, there are no guarantees in which order the parts of our code will run (race conditions)
• Many OS provide an API for creating new threads
• Many programming languages have their own special implementaton of threads
• These are sometimes called lightweight or green threads
• Language takes # of green threads and execute them in different # of OS threads
• This process is called the M:N model, M green threads per N OS threads
• Rust needs to have nearly no “runtime” (baggage for the feature to perform correctly in the binary) for its threading; performance is critical
• Some crates implement green threading if that is desired
• To spawn a thread, call std::thread::spawn and pas it a closure with the code we wish to run

• /(On my PC, I had to use this example to actually see anything output as the main thread would finish before spawning the new thread:

use std::thread;

fn main() {
thread::spawn(|| for i in 1..300 {
println!("hi number {} from spawned thread!", i);
});

for i in 1..200 {
println!("hi number {} from main thread!", i);
}
}
• To ensure we wait for all threads to finish, we can use the JoinHandle returned from thread::spawn & call join on it to force the current thread to block until the thread belonging to the handle terminates

• By prefixing a closure with move, the closure is forced to take ownership of any values it uses from its environment rather than inferred borrowing

• This is done because if we refer to a variable in our closure’s environment, there is no way to know if it still exists or has been deallocated

#### Message Passing to Transfer Data Between Threads

• Message passing is where threads or “actors” communicate with each other by sending messages containg data
• A major mechanism of message passing is the channel, a tool split into two separate halves:
• The transmitter is used to send data
• The receiver is used to check for arriving messages
• The std::sync::mpsc::channel() function creates a new channel (returned as a tuple contained the transmitter & receiver
• mpsc stands for multiple producer, single consumer - we can have multple transmitters but only one receiver
• The transmitting channel has a send method that we can use to send messages
• The receiving channel has a recv() method that will block the thread it is called on until a message is received
• try_recv does not block
• Once a value has been sent to another thread with send, send takes ownership of that function meaning it can no longer be used in the calling thread
• Cause a thread to sleep with thread:sleep(Duration)
• We can iterate over the receiving channel instead of using recv
• mpsc means multiple producers - we can create new instances of the transmittng channel by calling clone on the variable containing the transmittng channel, tx

#### Shared State Concurrency

• Message passing isn’t the only way to deal with concurrency; one can also share memory
• If channels are analogous to single ownership, shared memory would be multiple ownership
• A mutex is a “concurrency primitive for sharing memory” - short for mutual exclusion
• Only allows one thread to access data at a given time
• Mutexes are known to be hard to use as one needs to remember to acquire a lock before using the data, and then to unlock the data when done with it
• In Rust, the primitive type is Mutex<T> from std::sync
• Mutex::new creates a new mutex
• Use lock to acquire a lock on that data - this will block current thread!
• Returns a Result since the call can fail if another thread that currently has the lock panics
• Lock returns a MutexGuard smart pointer, which implements the Deref trait to point as its inner data
• MutexGuard also has Drop implemented which will release the lock
• In order to share a Mutex between threads, it needs to be stored in an Arc<T> - similar to Rc<T> but the a stands for atomic
• Atomics are another kind of concurrency primitive that are thread-safe
• Everything in Rust isn’t atomic by default since being atomic carries a performance penalty
• Just like Rc<T>, we create new instances of our Arc<T> with clone
• Mutex<T> provides interior mutability like RefCell<T> does

#### Extensible Concurrency with the Sync and Send Traits

• std::marker contains two traits: Sync and Send
• Send indicates that ownership of that type may be transferred between threads
• Almost all types are Send (Rc<T> isn’t)
• Any type made up of Send types is automaticalyl marked Send as well
• Sync indicates that a type is safe to have references to its value from multiple threads
• T is Sync is &T is Send
• Primitives are Sync, types made up of Sync are also Sync
• Cell<T> family is also not Sync
• Implementng Send and Sync manually is unafe

### Is Rust an Object-Oriented Programming Language?

#### What Does Object-Oriented Mean?

• (Much of this chapter is going over OO concepts such as objects/encapsulation and polymorphism, skimmed through)
• Rust does not have inheritence; traits are used in its place to simulate similar behaviour

#### Trait Objects for Using Values of Different Types

• The problem to solve here is creating functions that take as an argument objects of different types yet with a similar interface
• How can we program a function that will work on user-defined types if the library author doesn’t know what objects the end-user developer may want to use
• In Rust, trait objects are used to accept as an argument or field any object that implements said trait and is behind a pointer (such as a reference & or a smart pointer like Box<T>)
• We can use traits as a trait object in places where we’d use a concrete or generic type
• While a generic type parameter can only be substituted for one concrete type at a time, trait objects allow for multiple concrete types to fill in for the trait object at runtime
• If all we need one is one type at a time, generic type parameters are still preferred
• Trait objects must be behind a smart pointer since we don’t actually know the size of the objects being provided to these structs and functions until runtime
• Concept is similar to duck typing in some dynamic languages
• Since Rust cannot possibly known all types that will be used with code using trait objects, it uses dynamic dispatch instead of static dispatch
• This means that instead of creating a separate function for each type at compile time, dynamic dispatch figures out what function to call at runtime, carrying a performance penalty
• Trait objects require object safety:
• Does not require Self to be Sized
• All methods are object safe, which means:
• Requires Self to be Sized ????
• Meets the following conditions:
• Not have any generic type parameters
• First argument must be of type Self
• Must not use Self anywhere else in the signature except the first argument
• Self is a keyword that is an alias for the type we’re implementing
• Sized is an implicit trait bound that says “the size of this type is known as compile time”
• Automatically implemented on types that have a known size at compile time
• Trait objects and slices [T] do not have a known size at compile time
• Opt out of Sized by defining T: ?Sized as a trait bound

#### Object-Orienated Design Pattern Implementation

• (Chapter goes over an example of implementing the state pattern)
• /tl;dr: All patterns have tradeoffs. Object-oriented patterns don’t work well in Rust due to features like ownership

### Patterns Match the Structure of Values

#### All the Places Patterns May be Used

• Patterns are found in many different places in Rust
• We often see them in arms in match expressions, where an arm is made up of a pattern followed by an expression to run
• In match arms, it’s common to see the last pattern as _, which matches everything but never binds a variable
• if let allows us to write the equivalent of a match statement that only cares about one arm / matching case
• Exhaustiveness is not checked
• while let is similar to if let; allows us to loop as long as the pattern in the condition matches
• for loops take a pattern - we can destructure an iterator (e.g.: enumerate) to have both an index and value returned when enumerating over a vector, for example
• let statements can also take a pattern where the variable name goes; can be used to instantiate multiple variables at once
• Function parameters can also be patterns - can be used to match a tuple as an argument, for example

#### Refutability: Whether a Pattern Might Fail to Match

• Patterns can be either refutable or irrefutable
• If a pattern cannot fail to match for any possible value, it’s said to be irrefutable (e.g.: let statements, function params, for loops)
• If a pattern can fail to match, it’s said to be refutable (e.g.: if let, while let, made to handle possible failure)
• Not something we have to generally worry about, good to be familiar with the ideas though
• The terms can be seen in some error messages (e.g.: trying to bind a refutable pattern like Some in a let statement)

#### All the Pattern Syntax

• Patterns can match directly against literals:

let x = 1;

match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}
• Named variables will match any value

• This isn’t well worded IMO; I think this means that we can use a named variable as a pattern we will try to match against
• match expressions can match multiple patterns by separating them with “or” |

• Match against inclusive ranges with the range operator ...

• Can only use ranges with numeric values or char values
• Can use patterns to destructure structs, enums, tuples and references

• To destructure is to break up a value into component pieces
• We can ignore entire values or parts of a value with _

• Ignore unused variables by prefixing its variable name with an underscore _

• When values have many parts, we can ignore parts we don’t need with the .. operator:

match origin {
Point { x, .. } => println!("x is {}", x),
}
• Matching against a pattern means the variables in the pattern are bound to a value, which may move the value into the match

• Using & in a pattern matches a reference
• If we want to actually create a reference instead of borrowing, use the ref keyword before the new variable name
• Match guards are an additional if expression that appears after the pattern in a match expression:

let num = Some(4);

match num {
Some(x) if x < 5 => println!("less than five: {}", x),
_ => (),
}
• We can both test a value in a pattern and bind it to a variable with the at @ operator:

enum Message {
Hello { id: i32 },
}

let msg = Message::Hello { id: 5 };

match msg {
Message::Hello { id: id @ 3...7 } => {
println!("Found an id in range: {}", id)
},
Message::Hello { id: 10...12 } => {
println!("Found an id in another range")
},
Message::Hello { id } => {
println!("Found some other id: {}", id)
},
}

The first arm allows us to both test that id is a value between 3 and 7 while also binding its true value to id if a match was found

#### Unsafe Rust

• Unsafe Rust is similar to safe Rust except no memory safety guarantees are made
• Static analysis of code used by Rust is conservative - sometimes we need to tell the compiler “Don’t worry, I know what I’m doing”
• Unsafe is necessary for the lowest level of operations (e.g.: writing an OS, interfacing with hardware)
• Unsafe Rust enables a couple of new features:
• Dereferencing a raw pointer
• Calling an unsafe method or function
• Accessing or modifying a mutable static varibable
• Implementing an unsafe trait
• Unsafe doesn’t disable features such as the borrow checker
• Keep unsafe blocks small to make debugging problems easier
• Wrap unsafe code within safe abstractions & APIs
• Raw pointers in unsafe Rust have two types:
• *const T is an immutable raw pointer
• *mut T is a mutable raw pointer
• We can create raw pointers in safe Rust but we cannot dereference the raw pointer or read it’s data
• We can have both immutable and mutable raw pointers in scope at the same time
• Big use is interfacing with C code
• Unsafe methods and functions are prefied with unsafe
• Bodies are essentially unsafe blocks
• Methods like as_mut_ptr return a mutable raw pointer to the object it was called on
• Rust exposes a Foreign Function Interface (FFI) to & from other languages using the extern keyword
• Always unsafe to call code declared with extern from safe Rust

• /This chapter goes over three concepts: lifetime subtyping, lifetime bounds & trait object lifetimes. I’m going to skip lifestime subtyping at this moment/
• Lifetime bounds are similar to trait bounds except we bind by lifetime instead of by trait: used when combining lifetimes with generic type parameters
• e.g.: struct Ref<'a, T: 'a>(&'a T); says T can be of any type but if T contains references, T must live as long as 'a

• Associated types associate a type placeholder with a trait so trait method definitions can use the placeholder types in their signatures

• e.g.: The Iterator trait

pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}

Iterator has an associated type named Item, which is a placeholder type. When we implement Iterator, we define the type of Item with type Item = u32 for example

• Can be cleaner than generic traits if our use case has us implementing a trait for some object with a single concrete type vs. the ability to have many types with generics

• Can also result in less ugly looking function signatures over generics since we’d only have to declare an associated type to access its defined types vs a generic placeholder for each type

• We can also use associated types with trait objects but we need to constrain the trait object by defining the concrete types to take places of our associated types:

fn traverse(graph: &AGraph<Node=usize, Edge=(usize, usize)>) {}
• We can overload operators by implementing their traits from std::ops for whatever struct/enums we create

• We cannot create our own operators or overload arbitrary operators
• This works in part by using the power of associated types!
• If multiple traits are implemented on a type that have methods with the same name, we can use fully qualified syntax to specify which version of a method we wish to use: <Baz as Foo>::f(&b); calls Foo::f

• If a trait relies on another trait also being implemented on a type, the required trait is called a supertrait: trait OurTrait: SuperTrait { ... }

• A Newtype is a pattern allowing one to get over the “trait or type must be local to implement a trait” rule

• Create a type that is just a tuple wrapping our desires type we wish to implement the trait on, and then just implement the trait on the Newtype
• No runtime penalty; the wrapper is shedded at compile time
• Our Newtype won’t have any of our wrapped type’s methods; if we need them, we can implement Deref to return the inner type or just create proxy methods on the wrapper type

• The Newtype pattern discussed in the previous section to statically enforce that values are not confused (e.g.: A Meter type not being confused for a u32 as an function argument)
• Newtype can also be used to abstract away implementation details of a type by wrapping it into an object that exposes a different API
• A way to achieve encapsulation
• We can define a type alias to give an existing type another name: type Meters = i32;
• Meters is not a separate value, treated the same as i32
• Used to reduce repetition
• Rust has a special type known as the bottom type ! or the never type that is used in place of a function’s return type when the function never returns
• continue keyword has a value of !
• “The never type unifies with all other types.”
• loop and panic! both have a value of ! as well
• This is how both continue and panic! can be used in match statement arms; they return no value therefore their return value is never in conflict with other arms
• Dynamically sized types are types whose size can only be known at runtime
• str is a DST; this is why we can never instantiate a str directly
• DST must always be behind a pointer of some kind
• Sized is a trait automatically implemented for types whose size is known at compile time
• There is also an opposite trait bound called ?Sized that means T may or may not be sized

• Regular functions can be passed to functions as well
• They have the type fn
• Called function pointers
• Can be passed to functions expecting Fn-type closures so always write functions to accept those as arguments
• Only accept fn in cases where we’re interfacing with languages that dont have closures like C
• Closures cannot be returned from functions unless using trait objects

### Final Project: Building a Multithreaded Web Server

#### A Single Threaded Web Server

• Setting up a simple web server that works on the TCP level
• TcpListener in std::net allows us to listen for TCP connections
• TcpListener::incoming allows us to iterate over a sequence of streams
• A stream represents an open connection between client & server
• A connection is the name for the full request/respoonse process where client connects to server, server generates a resonse and then closes the connection
• We need to unwrap a stream to use it since we’re actually iterating over connection attempts, not actual connections
• When a stream goes out of scope, the connection is dropped
• Reading from a stream requires a buffer of a fixed size (e.g.: 512 bytes) and pass that buffer to stream.read(&mut buffer)
• String::from_utf8_lossy takes a &[u8] and returns a String
• Called “lossy” as it replaces invalid characters with U+FFFD
• The request will take the following format:
• Method Request-URI HTTP-Version CRLF
• message-body
• The response should have the following format:
• HTTP-Version Status-Code Reason-Phrase CRLF
• message-body
• How do we write data to the stream to send back to the connecting user?
• Create a response as a string/string slice
• stream.write(response.as_bytes()).unwrap() to write the data to the buffer as bytes
• stream.flush().unwrap() to wait until all bytes are written to connectoin
• What if we want to send a file over the wire?
• Open the file with File::open
• Create a new String that will act as a buffer
• Use file.read_to_string(&mut contents) to write the bytes in the file into the created String (returns a Result)
• Write the buffer to our stream as usual
• Returning content based on request URI
• Remember that the request starts with the following format: “Method Request-URI~
• We can use these two pieces of information to check if a buffer starts with these values with something like buffer.starts_with(b"GET / HTTP/1.1\r\n")
• We use b"string" since we are working on the level of raw bytes

#### How Slow Requests Affect Thoroughput

• As it is now, each request gets processed one at a time; this won’t scale well
• Available threads are assigned work
• When a thread is done working, it is assigned back into the pool of available threads
• We will use a thread pool to process multiple connections concurrently

#### Designing the Thread Pool Interface

• (Goes over the code for creating a simple ThreadPool struct)

• We start by adding an assert! of size > 0 in the neW associated function for ThreadPool to ensure that we can have a thread pool size that makes sense
• thread::spawn returns a JoinHandle<T> where T is the type returned from the closure
• Option::take moves a value out of Some variant and leaves a None variant in its place