# Rust Book
doc.rust-lang.org/book (opens new window)
# 1. Ownership
- immutable variable can be many
- mutable variable only exist one at one time
- last usage ends, the variable scope ends
- reference is borrow
- fn vs method
- fn ’s parameters are anything;
- method parameters: first is always self;
- method are defined in impl
- fn defined in impl don’t accept self, they are static/associate fn; ???TODO
// String slice
let my_string = String::from(“hello”);
let my_slice = &my_string[..];
// Array slice
let a = [1, 2, 3, 4];
let a_slice = &a[1..3];
2
3
4
5
6
# 2. Structs
- Defination
struct User {email: String, name: String}
// unit struct
struct UnitExample;
// tuple struct
struct User(String, String)
2
3
4
5
- Instance
let user1 = User {
name: String::from(“naaa”),
email: String::from(“eee”)
};
let user2 = User(String::from(“name”), String::from(“email”));
let user3 = User{
name: String::from(“userss”),
..user2
};
2
3
4
5
6
7
8
9
# 3. Enum
- Defining an Enum
- enum variant can be any kind
- enum variants are of the same type
- You can use Some(T)/None directly.
enum Option<T> {
Some(T),
None,
}
2
3
4
- Match
- if let is a syntax sugar when match matches the first arm and ignores the rest.
// match version
let mut count = 6;
match coin {
Coin::Quater(state) => println!("State from {:?}", state),
_ => count += 1,
}
// if let version
let mut count = 0;
if let Coin::Quater(state) = coin {
println!("State from {:?}", state)
} else {
count += 1;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 4. Packages, Crates and Modules
Cargo new my-project:
- creates a package, can have multiple binary crates.
- with src/main.rs which is a binary crate.
- with src/lib.rs which is a library crate.
- files in src/bin/ are separate binary crate.
- src/main.rs and src/lib.rs are called crate roots
- the entire module tree is rooted under the implicit module named crate.
Packages: A Cargo feature that lets you build, test, and share crates
- A package is one or more crates that provide a set of functionality.
- A package contains a Cargo.toml file that describes how to build those crates.
- A package can contain one library crate at most.
- It can contain many binary crates.
- it must contain at least one crate (either library or binary).
Crates: A tree of modules that produces a library or executable
- A crate is a binary or library.
- Group related functionality together in a scope to share.
- Only crates get compiled.
- rustc --crate-type=lib rary.rs (opens new window) can override compile type.
// This crate is a library #![crate_type = "lib"] // The library is named "rary" #![crate_name = "rary"]
1
2
3
4Modules and use: Let you control the organization, scope, and privacy of paths
- define a module: mod your_mode_name {}
- Inside modules, we can have other modules
- child mod can use parent mod items, the otherwise can't.
- to make an item private, you put it in a mod.
- filename can be mod name.
Paths: A way of naming an item, such as a struct, function, or module
- the use keyword that brings a path into scope
- use crate::front_of_house::hosting; then you can use hosting::anything directly.
- relative path: use self::front_of_house::hosting;
- use std::io::Result as IoResult;
- use std::{cmp::Ordering, io}; use std::io::{self, Write}; use std::collections:😗;
- the pub keyword to make items public.
- pub use:
Organise your project:
- touch src/test_fun.rs
- add
pub mod test_fun
in src/lib.rs - add
use your_project::test_fun;
when you use it;
Attributes:
// apply to the whole crate #![crate_attribute] // apply to module/item #[item_attribute] // different syntaxes #[attribute = "value"] #[attribute(key="value")] #[attribute(value)] // multiple values #[attribute(value, value2)] // multiple lines #[attribute(value, value1, value2, value3)] // ===DeadCode=== #[allow(dead_code)] // ===cfg=== // two different ways to use config // 1. in attribute position #[cfg(target_os = "linux")] #[cfg(not(target_os = "linux"))] // 2. macro as a boolean expression if cfg!(target_os = "linux") {...} // custom cfg // rustc --cfg some_condition should be used #[cfg(some_condition)] fn conditional_function() { println!("condition met!"); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 5. Common Collections
vector:
Vec<T>
let mut v: Vec<i32> = Vec::new(); let mut v = vec![1, 2, 3]; v.push(4); v.get(2); // option<i32>; let third: &i32 = &v[2];
1
2
3
4
5string
let mut s = String::new(); s.push('l'); s.push_str("bar"); let ss = "initial contents".to_string(); let ss = String::from("initial contents"); // concatenation let s1 = String::from("Hello, "); let s2 = String::from("world!"); let s3 = s1 + &s2; // format! let s = format!("{}-{}", s1, s2); // iteration for c in "नमस्ते".chars() { println!("{}", c); } for b in "नमस्ते".bytes() { println!("{}", b); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21hash map
use std::collections::HashMap; // new let mut scores = HashMap::new(); // insert scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50); // overwrite scores.insert(String::from("Blue"), 25); let team_name = String::from("Blue"); // get let score = scores.get(&team_name); // iteration for (key, value) in &scores { println!("{}: {}", key, value); } // or_insert scores.entry(String::from("Yellow")).or_insert(50);// not insert scores.entry(String::from("Green")).or_insert(50);// insert
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 6. Error Handling
unrecoverable: panic!
recoverable:
Result<T, E>
use std::fs::File; fn main() { let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => panic!("Problem opening the file: {:?}", error), }; // unwrap let f = File::open("hello.txt").unwrap(); // expect let f = File::open("hello.txt").expect("Failed to open hello.txt"); } // the ? Operator #![allow(unused)] fn main() { use std::fs::File; use std::io; use std::io::Read; fn read_username_from_file() -> Result<String, io::Error> { let mut s = String::new(); File::open("hello.txt")?.read_to_string(&mut s)?; Ok(s) } } // only allowed to use the ? operator in a function that returns Result // or Option or another type that implements std::ops::Try
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33- To panic or Not
it’s appropriate to panic in examples, prototype code, and tests.
# 7. Generic Types, Traits and Lifetimes
Traits: Define shared behavior.
- trait 像是公共方法的模板
// trait pub trait Summary { fn summarize(&self) -> String; } // default impl pub trait Summary { fn summarize(&self) -> String { String::from("(default impl of Summary)") } } impl Summary for NewsArticle { fn summarize(&self) -> String { // do something to override default impl } } // item impl Summary as parameter pub fn notify(item: &impl Summary) { println!("Breaking news! {}", item.summarize()); } ↕️ // trait bound pub fn notify<T: Summary>(item: &T) { println!("Breaking news! {}", item.summarize()); } // Multiple Trait Bounds pub fn notify(item: &(impl Summary + Display)) { pub fn notify<T: Summary + Display>(item: &T) { // where fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 { fn some_function<T, U>(t: &T, u: &U) -> i32 where T: Display + Clone, U: Clone + Debug { // as return type fn returns_summarizable() -> impl Summary {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40- Lifetimes
every reference in Rust has a lifetime, which is the scope for which that reference is valid
// Lifetime Annotation Syntax &'a mut i32 // In fn signatures fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { // In Struct Definitions struct ImportantExcerpt<'a> { part: &'a str, } // Lifetime Elision Rules 1. input lifetimes: each reference parameter gets its own lifetime parameter 2. output lifetimes: 1. if there is one input lifetime parameter, that lifetime is assigned to all output lifetime parameters 2. In methods the lifetime of self is assigned to all output lifetime parameters // 'static the lifetime of all string literals is 'static
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 8. Automated Tests
How to write tests
the attribute: #[test] indicate the fn is a test function.
assert!, assert_eq!, and assert_ne!
#[test] #[should_panic(expected = "Guess value must be less than or equal to 100")]
1
2
Control how tests are run
- $ cargo test -- --test-threads=1
- $ cargo test -- --show-output
- $ cargo test add // tests name contain add
- #[ignore]
- $ cargo test -- --ignored // only run ignored
Test Organization
unit tests
to test each unit of code in isolation to pinpoint code work as expected
#[cfg(test)] mod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); } }
1
2
3
4
5
6
7
integration tests
- can only call functions that are part of your library’s public API
- tests directory
src| lib.rs tests|common|mod.rs // common is a submod that any i_t file can use tests|integration_test.rs
1
2
3
4- Only library crates expose functions that other crates can use
- eprintln! print error on the screen.
- cargo run to poem.txt > output.txt => store std::output to a file.
# 9. Iterators and Closures
Closures
- thread::sleep(Duration::from_secs(2));
struct Cacher<T> where T: Fn(u32) -> u32,// T is a closure { calculation: T, value: Option<u32>, }
1
2
3
4
5
6
7- Capturing the ENV
fn main() { let x = 4; // if changed to fn, it wil not compile let equal_to_x = |z| z == x; let y = 4; assert!(equal_to_x(y)); let y = vec![1, 2, 3]; let equal_to_y = move |z| z == y; // use move ownership }
1
2
3
4
5
6
7
8
9
10
11
12
13Iterators
// Iterator trait pub trait Iterator { // Item is the type returned from iterator type Item; fn next(&mut self) -> Option<Self::Item>; } #[cfg(test)] mod tests { #[test] fn iterator_demonstration() { let v1 = vec![1, 2, 3]; // creates an iterator over items in v1 let mut v1_iter = v1.iter(); // next() change iter internal state // .next() value is immutable ref of v1 assert_eq!(v1_iter.next(), Some(&1)); assert_eq!(v1_iter.next(), Some(&2)); assert_eq!(v1_iter.next(), Some(&3)); assert_eq!(v1_iter.next(), None); // takes ownership of v1 and returns owned values let mut v1_iter = v1.into_iter(); // iterate over mutable refs let mut v1_iter = v1.iter_mut(); } } fn main() { let v1: Vec<i32> = vec![1, 2, 3]; let v2: Vec<_> = v1.iter().map(|x| x + 1).collect(); assert_eq!(v2, vec![2, 3, 4]); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 10. Cargo, crates.io
Customize your build through release profiles
// Cargo.toml // override default settings in diff profiles [profile.dev] opt-level = 0 [profile.release] opt-level = 3
1
2
3
4
5
6
7Publish libraries on crates.io (opens new window)
- Documentation comments use three slashes: ///
- Comments describe the entire crate: //! in src/lib.rs
- pub use reexport/reconstruct your project
Organize large projects with workspaces
- workspaces can help manage multiple related packages.
- crates share one target directory.
[workspace]
members = [
"02_concepts_is_a_crate",
]
2
3
4
5
- Install binaries from crates.io (opens new window)
- Extend Cargo using custom commands
# 11. Smart Pointers
Using
Box<T>
to Point to Data on the Heap- Store Data on the Heap
fn main() { // Store an i32 value on the heap using a box let b = Box::new(5); println!("b = {}", b); }
1
2
3
4
5Box<T>
provide only the indirection and heap allocation
Treating Smart Pointers Like Regular References with the Deref Trait
- The Deref trait allows you to customize the behavior of the dereference operator,
struct MyBox<T>(T); impl<T> MyBox<T> { fn new(x: T) -> MyBox<T> { MyBox(x) } } use std::ops::Deref; impl<T> Deref for MyBox<T> { type Target = T; fn deref(&self) -> &T { &self.0 } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17- Deref coercion
Running Code on Cleanup with the Drop Trait
- Drop lets you customize what happens when a value is about to go out of scope
- Variables are dropped in the reverse order of their creation
- Force a value to be dropped before the end of its scope: std::mem::drop
Rc, the Reference Couted Smart Pointer
Rc<T>
enable multiple ownership- The
Rc<T>
type keeps track of the number of references to a value:using Rc<T>
is only for use in single-threaded scenarios- we call
Rc::clone
, the reference count to the data within theRc<List>
will increase - fn
Rc::strong_count
get the reference count. - the Drop trait decreases the reference count automatically
Rc<T>
allows only immutable borrows checked at compile time;
RefCell and the Interior Mutability Pattern
RefCell<T>
is only for use in single-threaded scenarios, has single owner.- With references and
Box<T>
, the borrowing rules’ invariants are enforced at compile time. WithRefCell<T>
, these invariants are enforced at runtime. RefCell<T>.borrow() ⇒ Ref<T>; RefCell<T>.borrow_mut() ⇒ RefMut<T>
.- The
RefCell<T>
keeps track of how manyRef<T>
andRefMut<T>
smart pointers are currently active. - Every time we call borrow, the
RefCell<T>
increases its count RefCell<T>
lets us have many immutable borrows or one mutable borrow at any point in time
Reference Cycles Can Leak Memory
Rc<T>::downgrade() ⇒ Weak<T>
with weak_countWeak<T>::upgrade() ⇒ Option<Rc<T>>
with strong_count
# 12. Concurrency
Using Threads to Run Code
let handle = thread::spawn(|| { // do something }); // block handle.join().unwrap();
1
2
3
4
5
6Using Message Passing to Transfer Data Between Threads
use std::sync::mpsc;// multiple producer single consumer fn main() { let (tx, rx) = mpsc::channel(); let tx1 = mpsc::Sender::clone(&tx); }
1
2
3
4
5
6Shared-State Concurrency
use std::sync::Mutex;// mutual exclusion fn main() { let m = Mutex::new(5); { let mut num = m.lock().unwrap(); *num = 6; } println!("m = {:?}", m); } // Arc<T> is used in concurrent situations like Rc<T>, a is atomic use std::sync::{Arc, Mutex}; use std::thread; fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {}", *counter.lock().unwrap()); } // Similarities Between RefCell<T>/Rc<T> and Mutex<T>/Arc<T>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39Extensible Concurrency with the Sync and Send Traits
# 13. OOP
- Characteristics
- Using Trait Objects That Allow for Values of Different Types
- Implementing an OO Design Pattern
# 14. Patterns and Matching
- All the Places Patterns Can Be Used
- Refutability: Whether a Pattern Might Fail to Match
- Pattern Syntax
# 15. Advanced Features
- Unsafe Rust
- Advanced Traits
- Advanced Types
- Advanced Functions and Closures
- Macros
# 16. Final Project: Building a Multithreaded Web Server
# 17. Appendix
A
B
C
D - Useful Development Tools
// install rustfmt rustup component add rustfmt // format cargo project cargo fmt // use rustfix to fix your code cargo fix // lints with Clippy // install rustup component add clippy // run clippy lints cargo clippy // Rust Language Server(rls) rustup component add rls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16E
F
G - How Rust is Made and “Nightly Rust”
// “stability without stagnation”, Train Schedule nightly: * - - * - - * - - * - - * - - * - * - * | | beta: * - - - - - - - - * * | stable: * rustup toolchain install nightly rustup toolchain list rustup override set nightly
1
2
3
4
5
6
7
8
9
10