1. 1. Simple Project and Cargo Project
1.1. Cargo
1.2. Simple
2. 3. Common Programming Concepts
2.1. Variables and Mutabillity
2.2. Data Types
2.3. Functions
2.4. Control Flow
2.4.1. if expression
2.4.2. loops
2.4.2.1. loop
2.4.2.2. for
2.4.2.2.1. can own a iteration value
2.4.2.3. while
3. 4. Ownership
3.1. What is it?
3.2. References and Borrowing
3.3. Slice Type
4. 5. Data Struct
4.1. concepts
4.2. implementations
4.2.1. Instantiating Structs
4.2.1.1. struct name { attributes }
4.2.2. Method Syntax
5. 6. Enum
5.1. Enum
5.1.1. concepts
5.1.2. implementations
5.1.3. option enum
5.2. "match" Control Flow
5.3. If let control flow
6. 7. Packages, Crates, and Modules
6.1. Packages and Crates
6.2. Modules
6.3. Referring item in Module Tree
6.4. "use" keyword
6.5. Separating Modules
7. 8. Common Collections
7.1. Vectors
7.1.1. concept
7.1.1.1. Can hold any type Vec<T>
7.1.1.2. Allow storing more than one value in one data structure
7.1.1.3. put value next to each other in memory
7.1.1.4. Can only store same type values
7.1.1.4.1. can be combined with enum to store multiple types
7.1.1.5. dropping a vector also drop its elements
7.1.1.6. Store data on the heap
7.1.2. Implementation
7.1.2.1. get using index, references, get
7.1.2.1.1. index
7.1.2.1.2. references
7.1.2.1.3. get
7.1.2.2. push
7.1.2.2.1. add new element onto the end of vector
7.1.2.2.2. if there isn't enough room next to each other
7.1.2.3. pop
7.1.2.3.1. remove and returns the last element
7.1.2.4. iterating
7.1.2.4.1. using for loop, as a reference
7.1.2.4.2. we have to dereference the element first if we wanted to modify it
7.2. String
7.2.1. concepts
7.2.1.1. growable, mutable, owned, UTF-8
7.2.1.1.1. let hello = String::from("Hello");
7.2.1.1.2. let hello = String::from("Dobrý den");
7.2.1.1.3. let s = String::from("السلام عليكم");
7.2.1.1.4. let hello = String::from("שָׁלוֹם");
7.2.1.2. Types
7.2.1.2.1. String
7.2.1.2.2. string slice (string literal): &str
7.2.1.2.3. and many more: see doc!
7.2.1.2.4. each types can store text in different encoding or be represented in memory in different way
7.2.1.3. Indexing
7.2.1.3.1. Rust does not allow indexing string, because it's UTF-8
7.2.1.4. Internal representation
7.2.1.4.1. Collection of bytes
7.2.1.4.2. let hello = "Здравствуйте"; this has 24 bytes
7.2.1.4.3. Rust Perspective "नमस्ते"
7.2.2. Implementations
7.2.2.1. creating a string
7.2.2.1.1. String::from()
7.2.2.1.2. using string slice: "string".to_string()
7.2.2.1.3. let s = "String string"
7.2.2.2. Updating
7.2.2.3. Slicing
7.3. HasMap
7.3.1. concepts
7.3.1.1. Stores mapping of keys of type K to values of type V: HasMap<K, V>
7.3.1.2. useful when wanted to look up data not by index
7.3.1.3. Store data on the heap
7.3.1.4. homogeneous
7.3.1.4.1. All keys must have same type, also for values
7.3.2. implementations
7.3.2.1. creating HashMap
7.3.2.1.1. use std::collections::HashMap
7.3.2.1.2. .insert(K, V); for inserting data to HashMap
7.3.2.2. Ownership
7.3.2.2.1. values and keys can be owned by HashMap
7.3.2.3. Accessing Values
7.3.2.3.1. get(&k: K)
7.3.2.3.2. for loop
7.3.2.4. Updating HashMap
7.3.2.4.1. Overwriting
7.3.2.4.2. Selective insert
7.3.2.4.3. based on old value
8. 9. Error Handling
8.1. definitions
8.1.1. Requires to acknowledge an error before the code will compile
8.1.2. recoverable: file not found error
8.1.3. unrecoverable: bugs, trying to access a location beyond the end of an array
8.1.4. Rust doesn't have exception
8.2. panic!
8.2.1. concepts
8.2.1.1. Unrecoverable Error
8.2.1.2. execute -> unwind and clean up stack -> quit
8.2.1.3. happened when a bug of some kind detected
8.2.1.4. not clear to programmer how to handle the error
8.2.2. Implementations
8.2.2.1. panic! macro
8.2.2.1.1. error message
8.2.2.1.2. location of it in files
8.2.2.2. Backtrace
8.2.2.2.1. RUST_BACKTRACE=1
8.3. Result
8.3.1. concepts
8.3.1.1. Recoverable Errors
8.3.1.2. enum Result<T, E> T stands for response and E for error
8.3.2. implementations
8.4. When to use
9. 10. Generic Types, Traits, and Lifetimes
9.1. Generic Data Types
9.1.1. concepts
9.1.1.1. can be use with many different concrete data types
9.1.1.2. permormance
9.1.1.2.1. monomorphization
9.1.1.2.2. doesn't run any slower
9.1.2. implementations
9.1.2.1. defining method
9.1.2.1.1. fn largest<T>(list: &[T]) -> T this will return T types
9.1.2.2. defining struct
9.1.2.2.1. struct Point<T> { x: T, y: T, } in this struct, x and y have same types which is T
9.1.2.2.2. struct Point<T, U> { x: T, y: U, }
9.1.2.3. defining enum
9.1.2.3.1. enum Option<T>
9.1.2.3.2. enum Result<T,E>
9.2. Traits
9.2.1. concepts
9.2.1.1. works like interface in other language
9.2.1.2. shared behavior
9.2.2. implementations
9.2.2.1. trait on a type
9.2.2.1.1. impl [trait name] for [type name] {}
9.2.2.2. trait as param
9.2.2.2.1. when parameter is some type that implement the trait
9.2.2.2.2. pub fn [name]<T: [trait]> ([varname]: T) {}
9.2.2.3. where clauses
9.2.2.3.1. pub fn [name]<T, U> ([varname]: T, [varname]: U) where T: [trait], U: [trait] {}
9.2.2.4. returning types that implement traits
9.2.2.4.1. only returning single type
9.2.2.5. traits bounds to conditionally implement methods
9.2.2.5.1. blanket implementations: implement trait for any type that implement another trait
9.3. Validating References: Generic Lifetimes Parameters
9.3.1. concepts
9.3.1.1. required us to annotate the relationships using generic lifetime parameters
9.3.1.1.1. ensure the actual references used at runtime will definitely be valid
9.3.1.2. prevent dangling references
9.3.1.3. borrow checker
9.3.1.3.1. compare scope to determine whether all borrows are valid
9.3.1.4. Elision Rules
9.3.1.4.1. each parameter that is a reference gets its own lifetime parameter
9.3.1.4.2. if there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters
9.3.1.4.3. if there are multiple input lifetime parameters, the lifetime of self is assigned to all output lifetime parameters
9.3.2. implementations
9.3.2.1. using ' and name of the generic lifetime after & before type. Ex: &'a i32
10. 11. Automated Test
10.1. concepts
10.1.1. used for unit test
10.1.1.1. test each unit of code isolation from the rest of the code to quickly pinpoint where code is and isn't working as expected
10.1.2. cargo test compiles code in test mode and runs the resulting test binary
10.1.3. body of test function
10.1.3.1. Set up any needed data or state.
10.1.3.2. Run the code you want to test.
10.1.3.3. Assert the results are what you expect.
10.2. implementations
10.2.1. write test
10.2.1.1. #[test]
10.2.1.2. #[should_panic]
10.2.1.2.1. #[should_panic(expected = "[panic message]")
10.2.1.3. combine with Result<T, E>
10.2.1.4. use super::*;
10.2.1.4.1. when test module is inner module
10.2.2. control
10.2.2.1. run in parallel or consecutively
10.2.2.1.1. cargo test -- --test-thread = 1 this will tell program not to use any parallelism, running only in one thread
10.2.2.2. #[ignore]
10.2.2.3. cargo test
10.2.2.3.1. cargo test [test_name]
10.2.2.3.2. cargo test -- --nocapture
10.2.3. Organization
10.2.3.1. convention way to do unit test
10.2.3.1.1. create a module named tests in each file to contain the test function and to annotate the module with cfg(test)
10.2.3.2. #[cfg(test)]
10.2.3.2.1. tells rust to compile and run the test code only when we run cargo test.
10.2.3.2.2. cfg -> configuration
10.2.3.2.3. (test) -> configuration option
10.2.3.3. rust allow to test private fuctions
10.2.3.4. Integration Test
10.2.3.4.1. put test directory at the top level, next to src
10.2.3.4.2. if the project is binary crate (only contains src/main.rs), we cant create integration tests in the tests directory because they are meant to be run on their own
10.2.3.4.3. only library crates expose function that other crates can use
11. 13. Functional Language Features: Iterator and Closures
11.1. Closures
11.1.1. concepts
11.1.1.1. function-like construct we can store in a variable
11.1.1.2. it contains definition of a fuction, not resulting value
11.1.1.3. can be combined with generic type
11.1.1.4. can capture environtment
11.1.1.4.1. it uses memory to store the values for use in the closure body
11.1.1.5. Rust infers which trait to use based on how the closure uses the values from the environment
11.1.1.5.1. All closure implement FnOnce
11.1.1.5.2. didn't move captured variables implement FnMut
11.1.1.5.3. don't need mutable access implement Fn
11.1.1.6. only available inside scope it was defined
11.1.2. Implementations
11.1.2.1. let expensive_closure = |num| { println!("calculating slowly..."); num };
11.1.2.1.1. |...| symbol for parameters
11.1.2.1.2. can inference type
11.1.2.2. generic types
11.1.2.2.1. traits: Fn, FnMut, FnOnce
11.2. Iterator
11.2.1. concepts
11.2.1.1. way of processing a series of elements
11.2.1.2. LAZY
11.2.1.2.1. they have to perform some task on a sequence of items in turn
11.2.1.2.2. have no effect until we call methods that consume the iterator to use it up.
11.2.1.3. can be consumed
11.2.1.3.1. iter.sum()
11.2.1.3.2. looping
11.2.1.4. can produce other iterators
11.2.1.5. All iterators implement iterator trait
11.2.2. implementations
11.2.2.1. type
11.2.2.1.1. iter
11.2.2.1.2. into_iter
11.2.2.1.3. iter_mut
11.2.2.2. create iterator
11.2.2.2.1. let iter = collection.iter()
11.2.2.3. looping
11.2.2.3.1. for loop
11.2.2.3.2. next
11.2.2.4. filter
11.2.2.4.1. it takes a closure that takes each item from the iterator and returns a boolean
11.2.2.5. creating iterator using iterator trait
11.2.2.5.1. we can define what the iterator will do, ex. defining what next method will do
11.2.2.6. closure
11.2.2.6.1. filter
12. 14. Cargo and Crates.io
13. 15. Smart Pointers
13.1. definitions
13.1.1. pointers
13.1.1.1. example in rust is reference
13.1.1.2. don't have any special capabilities other than referring to data
13.1.1.3. don't have any overhead
13.1.1.4. variable that contains an address in memory
13.1.2. smart pointers
13.1.2.1. have additional metadata and capabillities
13.1.2.2. they own the data they point to
13.1.2.3. String and Vec<T> are smart pointers
13.1.2.3.1. both own some memory and allow us to manipulate it.
13.1.2.3.2. have metadatas: capacity, and ensuring its data will always be valid UTF-8 (for String)
13.1.2.4. Usually implemented using structs
13.1.2.4.1. implement Deref and Drop traits
13.2. Common Smart Pointers
13.2.1. Box<T>
13.2.1.1. concepts
13.2.1.1.1. Store data on the heap
13.2.1.1.2. box is pointer, therefore stored on the stack
13.2.1.1.3. since box is pointer, rust know how much space a box needs because a pointer size doesn't change based on the amount of data it's pointing to.
13.2.1.2. implementation: recursive types
13.2.1.2.1. without box
13.2.1.2.2. with box
13.2.2. Deref Trait
13.2.2.1. concepts
13.2.2.1.1. by implementing it, a smart pointer can be treated like a regular reference
13.2.2.1.2. we can write code that operates on references and use that code with smart pointers
13.2.2.1.3. following pointer to the value
13.2.2.1.4. deref method BORROWS self and returns a reference to the INNER DATA
13.2.2.2. implementations
13.2.2.2.1. assert_eq!(5, *y);
13.2.2.2.2. deref coercion
13.2.3. Drop Trait
13.2.3.1. concepts
13.2.3.1.1. included in the prelude
13.2.3.1.2. can provide an imlementation for the drop trait on any type
13.2.3.1.3. can put logic to execute when an instance goes out of scope
13.2.3.2. implementation
13.2.3.2.1. requires us to implement one method named drop that takes a mutable reference to self
13.2.3.2.2. Dropping a Value Early with std::mem::drop
13.2.4. Rc: Reference Counted
13.2.4.1. The Rc<T> type keeps track of the number of references to a value to determines wheteher or not a value is still in use
13.2.4.1.1. if == 0 -> the value can be cleaned
13.2.4.1.2. ensure that destructors are called for shared data
13.2.4.2. only for use in single threaded scenarios
13.2.4.3. shares ownership of data on the heap
13.2.4.3.1. readonly data
13.2.4.4. implementation
13.2.4.4.1. count
13.2.5. RefCell<T>
13.2.5.1. does not allocate, but it contains an additional “borrow state” indicator (one word in size) along with the data.
13.2.5.2. RefCell<T> enforces the read-write lock pattern at runtime (it’s like a single-threaded mutex)
13.2.5.2.1. borrow() and borrow_mut()
13.2.5.3. borrowing rules enforced at runtime
13.2.5.3.1. when we break the rules, it will panic
13.2.5.4. only for single threaded scenarios
13.2.6. Dealing with reference cycles
13.2.6.1. by turning Rc<T> to Weak<T>
13.2.6.1.1. upgrade
13.2.6.1.2. downgrade
14. 16. Concurrency
14.1. thread::spawn
14.1.1. move closure
14.1.1.1. take ownership of an instance from main thread
14.1.1.2. thread take closure with 0 parameters
14.1.1.3. syntax: move || {}
14.1.2. join handles
14.1.2.1. calling join() block the thread currently running until the thread represented by the handle terminates
14.1.2.2. block means the thread prevented from performing work or exiting
14.1.3. thread will end when the main tread ends
14.2. message passing: channel multiple producer single consumer (mpsc)
14.2.1. transmitter
14.2.2. receiver
14.2.2.1. recv
14.2.2.1.1. will block main thread's execution and wait untul a value is sent down the channel
14.2.2.1.2. recv will return a Result<T,E>
14.2.2.2. try_recv
14.2.2.2.1. doesn't block but will return a Result<T,E> immediatly
14.2.2.2.2. useful if this thread has other work to do while waiting for messages
14.3. Mutex: Shared-state Concurrency
14.3.1. it's like multiple ownership: multiple threads can access the same memory location at the same time
14.3.2. mutex
14.3.2.1. only allows one thread to access some data at any given time
14.3.2.2. access data
14.3.2.2.1. thread must first signal that it wants access by asking to acquire the mutex's lock
14.3.2.2.2. lock
14.3.2.3. rules
14.3.2.3.1. we must attempt to acquire the lock before using the data
14.3.2.3.2. when we're done with the data, we must unlock the data so other threads can acquire the lock
14.3.2.3.3. Rust type system and ownership rules, we can't get locking and unlocking wrong
14.3.2.4. example
14.3.2.4.1. use std::sync::Mutex; fn main() { let m = Mutex::new(5); { let mut num = m.lock().unwrap(); *num = 6; } println!("m = {:?}", m); }
14.3.2.4.2. use Arc<T>