2020-06-15 # [The Rust Programming Language](https://doc.rust-lang.org/book/) [正體中文版](https://rust-lang.tw/book-tw/) - Rust 1.41.0 + `edition="2018"` (*Cargo.toml* ?) * install/update → [Ch1](#11-Installation) * editions → Appendix E - 2018 Edition improvements included - Rust’s backward compatibility --- # Foreword # Introduction --- # 1. [Getting Started](https://doc.rust-lang.org/book/ch01-00-getting-started.html) gogo~ ## 1.1. [Installation](https://doc.rust-lang.org/book/ch01-01-installation.html) - install Rust tools by `rustup` - stability guarantees? - **Linux** * copy and run the `curl` command * requires a C compiler/linker (?? - **Windows** * download and run *[rustup-init.exe](https://www.rust-lang.org/tools/install)* * requires *[C++ build tools for Visual Studio](https://www.visualstudio.com/downloads/)* - after installed * update: `rustup update` * uninstall: `rustup self uninstall` * troubleshoot: `rustc --version` * local doc: `rustup doc` ## 1.2. [Hello, World!](https://doc.rust-lang.org/book/ch01-02-hello-world.html) - as usual * by command line * or your favorite IDE 💬 [Rust -- Tools](https://www.rust-lang.org/tools) - create directories (not required but ease) - run `main.rs` * naming convention: `hello_world.rs` (underscore to separate) * `rustc main.rs` * then, `./main` or `.\main.exe` - anatomy * entry point: `fn main()` (no parameter, return nothing) * coding style (there will be `rustfmt`) ```rust fn main() { } ``` * `println` * indent: 4 spaces (not tab) * macro/function (with/without `!`) * string: `"abc"` * end the line with `;` - compile * results to executable * can run without Rust installed * bigger projects require build tool -- *Cargo* ## 1.3. [Hello, Cargo!](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) - Cargo: build system + package (dependencies) manager * 💬 [Rustaceans](https://www.rustaceans.org/): Rust fans * check: `cargo --version` - create: `cargo new hello_cargo` * new Git repository and *.gitignore* + `--vcs` flag for different/no version control system + 💬 otherwise, [`cargo init`](https://doc.rust-lang.org/cargo/commands/cargo-init.html) in existing directory (simply `cargo new` does not work) * *Cargo.toml* in [TOML](https://github.com/toml-lang/toml) format + `[package]`: name, version, author (fix if not correct), edition (Appendix E) + `[dependencies]` * *src/main.rs* + place source files under *src/* directory, top-level is for README, license, config, etc. + everything should be in its place - build: `cargo build` * */target/debug/hello_cargo* or *\target\debug\hello_cargo.exe* * *Cargo.lock* tracks versions of dependencies (auto generated, do not edit!) * `cargo run`: build (if required) and run * `cargo check`: compile without making executable (good for developing) - release: `cargo build --release` * */target/release* * optimize (run faster but build slower) - easier to maintain larger projects * the convention # 2. [Programming a Guessing Game](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html) - learning by doing: guessing game - 💬 [step by step commits](https://github.com/yipo/rust-book-project-chapter/tree/ch02-guessing-game) [iter #0](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html#setting-up-a-new-project) - **new project** 1. `cargo new guessing_game` 2. check *Cargo.toml* 3. check *src/main.rs* 4. `cargo run` (rapidly iterate) - 💬 version control `Cargo.lock`? [Cargo.toml vs Cargo.lock](https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html) * binary → yes; library → no [iter #1](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html#processing-a-guess) - **prompt** * bring `io` library into scope: `use std::io` + `std::prelude` * `fn` * `println!` - **variable** * create a variable: `let`/`let mut` (immutable by default) * comment: `//` * return a new `String` instance: `String::new()` + `String` is growable, UTF-8 encoded + `::` (associated) and static method + `new` convention - **read line** * `std::io::stdin` if lack of `use std::io` * [`std::io::Stdin`](https://doc.rust-lang.org/std/io/struct.Stdin.html) * `&` (reference) and `mut` (mutable) - **handle error** * break long lines * `Result`s + `read_line()` returns `io::Result` + generic [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html) * enum + be one of **a fixed set of values** (*variants*) → Ch6 + `Ok` (success and the output) or `Err` (fail and the reason) * `.expect()` + `Ok`: return the output (here, the number of bytes entered) + `Err`: crash and display the message + must be used + more error handling → Ch9 - **print value** by `{}` - **test** using `cargo run` [iter #2](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html#generating-a-secret-number) - **using crate** * Rust team provide `rand` crate (not in `std`) * binary/library crate * add the line under `[dependencies]` section + [semantic version](https://semver.org/) (*SemVer*) + `0.5.5` = `^0.5.5` * `cargo build` + registry: [crates.io](https://crates.io/) + `libc`, `rand_core` are `rand`'s dependencies + rebuild only the changed part - **reproducible build** with *Cargo.lock* * not upgrade unless explicitly do * `cargo update`: looks for `0.6.0` > ver > `0.5.5` * `rand = "0.6.0"` to use `0.6.x` * more → Ch14 - **generate random number** * `use rand::Rng` and `Rng` trait → Ch10 * `rand::thread_rng()` + `.gen_range(1, 101)` * (`cargo doc --open`: build doc of dependencies) * print the number and test [iter #3](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number) - **compare** * `use` `std::cmp::Ordering` enum: `Less`/`Greater`/`Equal` * call `.cmp()` on everything can be compared * `match` expression → Ch6/18 + *arm* and *pattern* + checked in turn * mismatched types (strong/static type system) + type inference: string and number (`i32` by default) - **fix** * *shadow* → Ch3 * `.trim()` to deal with `5\n` * `.parse()` + `let guess: u32` 💬 how `.parse()` know the type? infer `secret_number`'s type? + `.expect()` again [iter #4](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html#allowing-multiple-guesses-with-looping) - infinite **loop** by `loop {}` * be sure to indent - **quit** by `break` * ctrl-c or typing `quit` is not good - **handle invalid input** * take `match` instead of `expect` that crashes * `Err(_)`: `_` to catch all * retry by `continue` - remove `println!` that prints the secret number # 3. [Common Programming Concepts](https://doc.rust-lang.org/book/ch03-00-common-programming-concepts.html) - the part of Rust that the same as other languages - keywords → Appendix A ## 3.1. [Variables and Mutability](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html) - *immutable* by default: push you to write concurrency in good way - error: `cannot assign twice to immutable variable` * frustrating... not mean you're not a good programmer 😌 * avoid bugs * easier to trace code - add `mut` to be mutable * trade-off: mutate the member **vs** get a new copy ### [Constants?](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants) - `const` ≠ `let` * cannot `mut`: always immutable * type *must* be annotated * in any scope (e.g. global) * const expr only (cannot be any value at runtime) 💬 like `#define`/`constexpr` in C/C++ - example ```rust const MAX_POINTS: u32 = 100_000; ``` * naming convention for constants * underscores in numeric for readability ### [Shadowing](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing) - it's ok to declare a new variable with the same name * no error/warning * not to assign; without `mut` * for some kinds of transformation * the type can be changed (but `mut` not) ## 3.2. [Data Types](https://doc.rust-lang.org/book/ch03-02-data-types.html) - scalar/compound - static type: decided at compile time * usually infer, sometimes must annotate 💬 C++: built-in `auto` ### [Scalar Types](https://doc.rust-lang.org/book/ch03-02-data-types.html#scalar-types) single value - **integer** * `i8`/`i16`/`i32`/`i64`/`i128` for signed * `u8`/`u16`/`u32`/`u64`/`u128` for unsigned * `isize`/`usize` are architecture depended * literal + type suffix: `42u8` + separator: `_` + `0xff`, `0o77`, `0b1111`, `b'A'` (`u8` only) * overflow + panic in debug mode + [`Wrapping`](https://doc.rust-lang.org/std/num/struct.Wrapping.html) - **floating point** * `f32`/`f64` - operation: `+`, `-`, `*`, `/`, `%` - **boolean** * `bool` (`true`/`false`) - **character** * single quote * 4 bytes; support Unicode → Ch8 ### [Compound Types](https://doc.rust-lang.org/book/ch03-02-data-types.html#compound-types) group multiple values - **tuple** * fixed length; variety types ```rust let t: (i32, f64, u8) = (50, 6.4, 1); ``` * *destructuring* ```rust let (x, y, z) = t; ``` ```rust let x = t.0; let y = t.1; let z = t.2; ``` - **array** * fixed length; same type ```rust let a = [1, 2, 3, 4, 5]; ``` * scenario + on stack (rather than heap) + ensure length -- e.g. 12 months (otherwise, `vector` → Ch8) * array's type ```rust let a: [i32; 5] = [1, 2, 3, 4, 5] ``` * initialize ```rust let a = [3; 5] // [3, 3, 3, 3, 3] ``` * access ```rust let x = a[0]; let y = a[1]; ``` + boundary check at runtime ## 3.3. [Functions](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html) - **function** * entry point: `fn main()` (`fn` to declare function) * naming convention: `snake_case` (all lowercase, underscore separate) * don't care the order function defined - **parameter** * *argument*s are values for *parameter*s (variables) * function signature * must declare the type: `fn func(x: i32)` * multiple arguments: `fn func(x: i32, y: i64)` (can be different type) - **function body**: **statement** × n (+ ending **expression**) * **statement** do not return value, cannot assign to a variable + `let` statement (different from C, Ruby; `x = y = 6` does not work) + function definition * **expression** results to a value + `5 + 6` + function call, macro call + `{}` with ending expression (without `;` at the end) - **return value** ```rust fn func() -> i32 { 5 } ``` * type: declare by `->` * value + ending expression (of function body) + or `return` early * `mismatched types` + `help: consider removing this semicolon` 💬 so nice + empty tuple `()` by default ## 3.4. [Comments](https://doc.rust-lang.org/book/ch03-04-comments.html) - `//`: comment until the end of line - more → Ch14 ## 3.5. [Control Flow](https://doc.rust-lang.org/book/ch03-05-control-flow.html) - branch (`if`/`else`) and loop (`loop`, `while`, `for`) - **if** ```rust if n < 5 { // true } else { // false } ``` * optional `else` block * condition must be `bool` (no implicit cast) - **else if** * check in turn * consider to use `match` → Ch6 - **if** in **let** 💬 as *ternary operator* (`?:`) do ```rust let x = if condition { 5 } else { 6 }; ``` * `if` is expression * types must be matched - **loop** ```rust let x = loop { if condition { break result; } }; ``` * forever, until `break` (or ctrl-c) * could `break` with value - **while** * clearer than `loop`+`if`+`else`+`break` - **for** each element in one collection ```rust for num in [10, 20, 30, 40, 50].iter() { println!("{}", num); } ``` * better then using `while` * safe: avoid incorrect index limit * fast: not to check the condition at runtime * concise * `Range` to count down ```rust for num in (1..4).rev() { println!("{}", num); } ``` ### [Summary](https://doc.rust-lang.org/book/ch03-05-control-flow.html#summary) - how about some homework - ready to move on: ownership # 4. [Understanding Ownership](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html) the way Rust manage memory (with safety guarantee) ## 4.1. [What is Ownership?](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html) - ways to manage memory * garbage collect * explicitly allocate/free * **Rust:** rules checked at compile time (no overhead at runtime) - encourage: keep at it! :::info ### [The Stack and the Heap](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#the-stack-and-the-heap) - Rust do care about stack/heap - both are parts of memory * **stack** + like a pile of plates + first in (push), last out (pop) + must have a known, fixed size + access the known address * **heap** + like seats in a restaurant + allocate empty space that big enough + require to search and book-keep + follow the pointer (slower) - closer data means faster - function call stack - ownership handles these ::: ### [Ownership Rules](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#ownership-rules) 1. each value has one variable as *owner* 2. only *one* owner at a time 3. when owner *out of scope*, value dropped ### [Variable Scope](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#variable-scope) - the range that an item is valid (like other languages so far) ### [The `String` Type](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#the-string-type) - require a data type stored on the heap (rather than stack) to illustrate * `String` as example, also apply on any others - difference * **string literal:** immutable, must be known while developing * **`String`:** can store unknown amount of text - `::from()` to create `String` from string literal * can be mutable * method syntax → Ch5 * namespace of module → Ch7 ### [Memory and Allocation](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#memory-and-allocation) - difference (again) * **string literal:** immutable, hard-coded, fast * **`String`:** mutable, need allocation... - allocation * **request:** `::from()` by us * **return:** (different ways...) `::drop()` when out of scope by Rust - simple right now; may unexpected with multiple variables #### [Ways Variables and Data Interact: Move](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#ways-variables-and-data-interact-move) - let's assign values - **integer:** fixed size, on the stack * simply make a copy - **`String`:** ptr to data (on the heap), len, (capacity) * simply make a copy (*shallow copy*) → *double free* error * copy the data as well (*deep copy*) → very expensive * Rust: *move* → let `s1` become invalid - Rust never auto deep copy #### [Ways Variables and Data Interact: Clone](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#ways-variables-and-data-interact-clone) - to *deep copy* → `.clone()` * indicator for something expensive #### [Stack-Only Data: Copy](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#stack-only-data-copy) - no `.clone()` but still copied? * because no different between deep/shallow copy (on the stack) - `Copy` trait * older variable still usable after assignment * otherwise, `Drop` trait - general rule of `Copy` * Boolean, integer, floating point, char * tuple only contains `Copy` ### [Ownership and Functions](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#ownership-and-functions) pass value to function = assign value ### [Return Values and Scope](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#return-values-and-scope) pass value to function, but not to give ownership? → *reference* ## 4.2. [References and Borrowing](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html) - not to take/return ownership → borrow by *reference* * caller: `&s` -- create a reference to `s` * callee: `&String` -- accept a reference - do not have ownership * do not have to return/drop ### [Mutable References](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references) - (immutable) reference cannot be modified → *mutable* reference * caller: `&mut s` -- create a mutable reference * callee: `&mut String` -- accept a mutable reference - to prevent *data race* * only one mutable reference in a scope + create new scopes by `{}` * either one mutable reference, or any immutable references ### [Dangling References](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#dangling-references) - checked at compile-time - no dangling guaranteed at run-time ### [The Rules of References](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#the-rules-of-references) 1. either... * one mutable reference, or * any number of immutable references 2. reference must always be valid ## 4.3. [The Slice Type](https://doc.rust-lang.org/book/ch04-03-slices.html) - *slice* type * reference a contiguous sequence * (also) do not have ownership - problem: find the first word - first try: return the index * pass without ownership -- `&String` * element by element -- `.as_bytes()` * iterator over it -- `.iter()` + with the index in tuple -- `.enumerate()` * destructure the tuple -- `(i, &item)` - cons: no guarantee to a valid string + even more for `second_word()` ### [String Slices](https://doc.rust-lang.org/book/ch04-03-slices.html#string-slices) - `&s[0..5]` -- *string slice* is * `&s` -- a reference * `[0..5]` -- to part of a string - `[begin..end]` * the `end` one is not included (length = end - begin) - shortcut * `&s[..2]` = `&s[0..2]` * `&s[3..]` = `&s[3..len]` * `&s[..] ` = `&s[0..len]` - rewrite `first_word()` * the *string slice* type -- `&str` - compile error reported if index used after string cleared * because of *borrowing*: ether one mutable, or any immutable references #### [String Literals Are Slices](https://doc.rust-lang.org/book/ch04-03-slices.html#string-literals-are-slices) a *string literal* (`&str`) is an *immutable reference* #### [String Slices as Parameters](https://doc.rust-lang.org/book/ch04-03-slices.html#string-literals-are-slices) `&str` parameter to receive both `&String` and `&str` ### [Other Slices](https://doc.rust-lang.org/book/ch04-03-slices.html#other-slices) also works on array slice (like `&[i32]`) → Ch8 ### [Summary](https://doc.rust-lang.org/book/ch04-03-slices.html#summary) - ensure memory safety at compile time: ownership, borrowing, slice - affect lots of other parts of Rust # 5. [Using Structs to Structure Related Data](https://doc.rust-lang.org/book/ch05-00-structs.html) - **`struct` (*structure*):** custom type to name and group related values * `struct` vs tuple * method and associated function * `struct` and `enum` → Ch6 ## 5.1. [Defining and Instantiating Structs](https://doc.rust-lang.org/book/ch05-01-defining-structs.html) - feature * pieces can be different types (same as tuple) * name each piece of data + clear meaning + access by name (rather than order) - usage * define a struct * create an instance + don't have to be same order * get a specific value - mutable struct: cannot be partial mutable - convenient shorthands... ### [Field Init Shorthand](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#using-the-field-init-shorthand-when-variables-and-fields-have-the-same-name) - simply `name` if variable/field have the same name * instead of `name: name` ### [Struct Update Syntax](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax) - `..user1` -- all the rest fields have the same value as `user1`'s ### [Tuple Structs](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#using-tuple-structs-without-named-fields-to-create-different-types) - have a name for entire struct - **but** no name for each field - become a new type * even corresponding fields are the same type - access the value: `.` + index ### [Unit-Like Structs Without Any Fields](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#unit-like-structs-without-any-fields) - the unit type: `()` - a trait without any data → Ch10 :::info ### [Ownership of Struct Data](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#ownership-of-struct-data) - `String` rather than `&str` in the struct * own the data as long as the struct is valid - store references in a struct? *lifetime* → Ch10 * `^ expected lifetime parameter` ::: ## 5.2. [An Example Program Using Structs](https://doc.rust-lang.org/book/ch05-02-example-structs.html) - when to use structs? example: calculate the rectangle area * refactor until using structs instead - **first try:** variables `width1` and `height1` * cons: one rectangle but two parameters ### [Refactoring with Tuples](https://doc.rust-lang.org/book/ch05-02-example-structs.html#refactoring-with-tuples) - **better one:** one tuple `rect1` * pros: a bit of structure * cons: `.0` and `.1` are confusing ### [Refactoring with Structs: Adding More Meaning](https://doc.rust-lang.org/book/ch05-02-example-structs.html#refactoring-with-structs-adding-more-meaning) - **much better:** one struct with `width` and `height` fields ### [Adding Useful Functionality with Derived Traits](https://doc.rust-lang.org/book/ch05-02-example-structs.html#adding-useful-functionality-with-derived-traits) - to see the values while debugging... `println!("{}", rect1);` 1. doesn't implement `std::fmt::Display` * may be able to use `{:?}` instead 2. doesn't implement `std::fmt::Debug` * add `#[derive(Debug)]` or implement `std::fmt::Debug` 3. add `#[derive(Debug)]` to explicitly opt in * `{:#?}` for pretty-print - many other traits for `derive` → Appendix C ## 5.3. [Method Syntax](https://doc.rust-lang.org/book/ch05-03-method-syntax.html) - *method*s are like functions * declared with `fn` keyword * have parameters and a return value * contain some code to be called - **but** within the context of a struct (enum or trait object) * first parameter is `self` (instance of struct) ### [Defining Methods](https://doc.rust-lang.org/book/ch05-03-method-syntax.html#defining-methods) - change `area` function to a method 1. move `area` function into `impl` (implementation) block of `Rectangle` 2. change first parameter to `&self` * use `self.` to access variables in the body 3. call `area` method by *method syntax* -- `.method_name(argument, …)` - `&self` instead of `rectangle: &Rectangle`? * Rust knows the type of `self` due to `impl Rectangle` - `&` before `self`? | first param | meaning | note | |:-----------:|:---------------- | ---- | | `self` | take ownership | rare | `&self` | borrow immutably | read only | `&mut self` | borrow mutably | write - benefit * *method syntax* * not to repeat the type of `self` * **for organization:** all the things we can do with the type are here + users don't search them in various places :::info ### [Where’s the `->` Operator?](https://doc.rust-lang.org/book/ch05-03-method-syntax.html#wheres-the---operator) - C++ * ` .method()` for an object * `->method()` for a pointer to an object + i.e., `(*ptr).method()` - Rust: *automatic referencing and dereferencing* * auto add `&`, `&mut`, or `*` to match the signature * no `->` * because methods have a clear receiver (💬 can Rust have overloading functions? ::: ### [Methods with More Parameters](https://doc.rust-lang.org/book/ch05-03-method-syntax.html#methods-with-more-parameters) practice: whether one rectangle can hold another? 1. name the method `can_hold` within the `impl Rectangle` block 2. take an immutable borrow of another `Rectangle` as a parameter 3. call the method by passing `&rect2` 4. return a `bool` value (by checking width and height) ### [Associated Functions](https://doc.rust-lang.org/book/ch05-03-method-syntax.html#associated-functions) - *associated function* * no `self` parameter (so not a method) * like `String::from` - often used for constructors * example: `Rectangle::square` * call by `::` syntax ### [Multiple `impl` Blocks](https://doc.rust-lang.org/book/ch05-03-method-syntax.html#multiple-impl-blocks) allowed to have multiple `impl` blocks → useful in Ch10 ### [Summary](https://doc.rust-lang.org/book/ch05-03-method-syntax.html#summary) - to create custom types: structs (and enum → Ch6) * name for clear meanings * methods and associated functions # 6. [Enums and Pattern Matching](https://doc.rust-lang.org/book/ch06-00-enums.html) - **`enum` (*enumeration*):** a type defined with its possible variants * (in Rust) similar to *algebraic data type*s of functional programming - `Option` can be either something or nothing - `match` values by patterns - `if let`: another idiom to handle enums ## 6.1. [Defining an Enum](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html) - situation: IP v4/v6 ```rust enum IpAddrKind { V4, V6, } ``` * be one of them (not both at the same time) ### [Enum Values](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html#enum-values) - create instances of `IpAddrKind` type: `IpAddrKind::V4`, `IpAddrKind::V6` * same on function parameters and arguments - concise than an enum in a struct -- attach data to enum variants ```rust enum IpAddr { V4(u8, u8, u8, u8), V6(String), } ``` * can have different types and amounts of associated data * standard library already do this → <code><a href="https://doc.rust-lang.org/std/net/enum.IpAddr.html">std::net::IpAddr</a></code> + can still have our own `IpAddr` due to namespace → Ch7 - another example: a wide variety of types * similar to different kinds of `struct` definitions * **but** under the `Message` type -- by which it's easily to accept any kinds of these * methods on enums ### [The Option Enum and Its Advantages Over Null Values](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html#the-option-enum-and-its-advantages-over-null-values) - to encode a value that could be something or nothing - the problem of *null*: use a null value as a not-null one * extremely common bugs (in other languages) - Rust does not have *null*s -- `Option<T>` ```rust enum Option<T> { Some(T), None, } ``` * so useful that... + in the prelude of [the standard library](https://doc.rust-lang.org/std/option/enum.Option.html) + use `Some` and `None` without `Option::` prefix * `<T>`: generic type parameter → Ch10 * example ```rust let some_number = Some(5); let some_string = Some("hello"); let absent_number: Option<i32> = None; ``` + `Some`: the type can be inferred + `None`: must be told - why? * `Option<T>` can't be used as `T` due to different types (can be checked by compiler) * must convert `Option<T>` to `T` (before doing any `T` operations) * must explicitly handle the case of null - how? * [a large number of methods](https://doc.rust-lang.org/std/option/enum.Option.html) for variety situations * `match`: run different code by variants ## 6.2. [The `match` Control Flow Operator](https://doc.rust-lang.org/book/ch06-02-match.html) - `match`: execute code based on which pattern matches * all kinds of patterns → Ch18 * pros + expressiveness + compiler confirms if all cases are handled * like a coin-sorter - example ```rust match coin { Coin::Penny => 1, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter => 25, } ``` * `match` + *expression* (can be any type) * each arm: *pattern* `=>` *some code* + compared in order + becomes the value `match` returns + `{ }` for multi-line code ### [Patterns that Bind to Values](https://doc.rust-lang.org/book/ch06-02-match.html#patterns-that-bind-to-values) - extract values out from enum variants - example: to collect different designs (for each state) of quarters ```rust match coin { … Coin::Quarter(state) => { println!("state: {:?}", state); 25 } } ``` ### [Matching with `Option<T>`](https://doc.rust-lang.org/book/ch06-02-match.html#matching-with-optiont) handle `Option<T>` by `match` just as other enums ### [Matches Are Exhaustive](https://doc.rust-lang.org/book/ch06-02-match.html#matches-are-exhaustive) any case not handled? compiler reports errors ### [The `_` Placeholder](https://doc.rust-lang.org/book/ch06-02-match.html#the-_-placeholder) - match all the rest * a unit value `()` to do nothing ## 6.3. [Concise Control Flow with `if let`](https://doc.rust-lang.org/book/ch06-03-if-let.html) - match one pattern and ignore all the rest (shortcut for `match`) * lose exhaustive check (trade-off) - `if let` *pattern* `=` *expression* `{ }` * `else` as `_` placeholder in `match` ### [Summary](https://doc.rust-lang.org/book/ch06-03-if-let.html#summary) - covered * `enum` * `Option<T>` to prevent errors * `match`/`if let` to extract values from enum - compiler ensures type safety # 7. [Managing Growing Projects](https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html) - organize is important (as a project grows) - techniques to group and encapsulate ## 7.1. [Packages and Crates](https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html) - **crate** * a binary or library * *crate root*: a source file that... + Rust compiler starts from + and makes up the root module of the crate + (for modules → Sec7.2) - **package** * one or more crates (provide a set of feature) + at least 1 crate (library or binary) + at most 1 library crate * contains *Cargo.toml* - create a package * `cargo new` → a package created with *Cargo.toml* * convention without specify the crate root (same name as the package) * **binary crate** + crate root: *src/main.rs* + other binary crates: place files in *src/bin* * **library crate** + crate root: *src/lib.rs* - for ease to share between projects - the scope of a crate ## 7.2. [Defining Modules to Control Scope and Privacy](https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html) - module system: module, path, `use`, `pub`, `as`, external package, glob operator - **module** * organize code into groups * control privacy (public/private) - example: restaurant * `cargo new --lib restaurant` * in *src/lib.rs* ```rust mod front_of_house { mod hosting { … } mod serving { … } } ``` + can have nested modules + and other definitions: struct, enum, constant, trait, or function - **module tree** * *crate root* -- the contents of *src/main.rs* and *src/lib.rs* + form the root module named `crate` * just like a filesystem ## 7.3. [Paths for Referring to an Item in the Module Tree](https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html) - path * **absolute path** (from a crate root) + `crate` or a crate name * **relative path** (from the current module) + `self`, `super` or an identifier in the current module * separated by `::` - example ```rust mod outer { mod inner { fn callee() { } } } pub fn caller() { crate::outer::inner::callee(); // absolute outer::inner::callee(); // relative } ``` - choice? * absolute: move separately * relative: move together - module also used for *privacy boundary* * all items are *private* by default * child can still access their ancestors * `pub` to make it *public* ### [Exposing Paths with the `pub` Keyword](https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#exposing-paths-with-the-pub-keyword) - the *callee* function should also be `pub` - *outer* (without `pub`) can be accessed by sibling ### [Starting Relative Paths with `super`](https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#starting-relative-paths-with-super) - `super`: relative path from the parent * like `..` in filesystem - why? in case the items are moved together to another module ### [Making Structs and Enums Public](https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#making-structs-and-enums-public) - `pub` struct * fields still be private (`pub` each one case-by-case) * require a `pub` function (to initialize private fields) - `pub` enum * all its variants are public ## 7.4. [Bringing Paths into Scope with the `use` Keyword](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#bringing-paths-into-scope-with-the-use-keyword) - bring paths into a scope by `use` * as if they're local items (like symbolic links in filesystem) * privacy is also checked * can also `use` by relative paths ### [Creating Idiomatic `use` Paths](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#creating-idiomatic-use-paths) - why not bring the *callee* directly? → should `use` the parent model instead * shows the item isn't locally defined * avoid name conflict (`fmt::Result`/`io::Result`) ### [Providing New Names with the `as` Keyword](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#providing-new-names-with-the-as-keyword) ```rust use std::io::Result as IoResult; ``` ### [Re-exporting Names with `pub use`](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use) - the item we bring into by `use` is private * combine `pub` to make it public (*re-exporting*) - why? provide different structure from internal one ### [Using External Packages](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#using-external-packages) - example: `rand` 1. list them in *Cargo.toml* ```toml [dependencies] rand = "0.5.5" ``` * Cargo downloads the package and its dependencies * then `rand` is available in our project 2. bring items from crates by `use` ```rust use rand::Rng; ``` - `std` is already shipped with Rust ### [Using Nested Paths to Clean Up Large `use` Lists](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#using-nested-paths-to-clean-up-large-use-lists) ```rust use std::io::{self, Write}; ``` ### [The Glob Operator](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#the-glob-operator) ```rust use std::collections::*; ``` - may hard to tell... * what names are in the scope * where a name was defined - often used when testing ## 7.5. [Separating Modules into Different Files](https://doc.rust-lang.org/book/ch07-05-separating-modules-into-different-files.html) ```rust mod outer { pub mod inner { pub fn callee() { } } } pub fn caller() { } ``` the module tree is the same as... - *src/lib.rs* ```rust mod outer; pub fn caller() { } ``` - *src/outer.rs* ```rust pub mod inner; ``` - *src/outer/inner.rs* ```rust pub fn callee() { } ``` ### [Summary](https://doc.rust-lang.org/book/ch07-05-separating-modules-into-different-files.html#summary) - package > crate > module - refer to items by absolute/relative paths - `use` paths - `pub` items (private by default) # 8. [Common Collections](https://doc.rust-lang.org/book/ch08-00-common-collections.html) - *collection* * contains multiple values * on the heap -- unknown amount, can grow or shrink * each kind has different capabilities and costs - these are *vector*, *string*, *hash map* and [more...](https://doc.rust-lang.org/std/collections) ## 8.1. [Storing Lists of Values with Vectors](https://doc.rust-lang.org/book/ch08-01-vectors.html) - *vector* -- `Vec<T>` * store values of the same type * for a list of items ### [Creating a New Vector](https://doc.rust-lang.org/book/ch08-01-vectors.html#creating-a-new-vector) - by `Vec::new` function ```rust let v: Vec<i32> = Vec::new(); ``` * a type annotation required * *generics* can hold any type - by `vec!` macro (with initial values) ```rust let v = vec![1, 2, 3]; ``` * the type can be inferred ### [Updating a Vector](https://doc.rust-lang.org/book/ch08-01-vectors.html#updating-a-vector) ```rust let mut v = Vec::new(); v.push(5); ``` - by `push` method - `mut` required - the type can be inferred ### [Dropping a Vector Drops Its Elements](https://doc.rust-lang.org/book/ch08-01-vectors.html#dropping-a-vector-drops-its-elements) - vector gets dropped when it goes out of scope * all its contents are also dropped * can be complicated when introduce references... ### [Reading Elements of Vectors](https://doc.rust-lang.org/book/ch08-01-vectors.html#reading-elements-of-vectors) - according to the behavior when the element not existed * `&v[2]` → a reference + panic + program crashed * `v.get(2)` → `Option<&T>` + not panic + returns `None` - (reference rules) the follows cannot in a same block * `&v[0]` -- immutable borrow * `v.push(6)` -- mutable borrow * why? may allocate bigger space and copy all the data + former references become invalid ### [Iterating over the Values in a Vector](https://doc.rust-lang.org/book/ch08-01-vectors.html#iterating-over-the-values-in-a-vector) - immutable ```rust let v = vec![1, 2, 3]; for i in &v { println!("{}", i); } ``` - mutable ```rust let mut v = vec![1, 2, 3]; for i in &mut v { *i += 1; } ``` * use dereference operator (`*`) to change the referenced value ### [Using an Enum to Store Multiple Types](https://doc.rust-lang.org/book/ch08-01-vectors.html#using-an-enum-to-store-multiple-types) - can be explicit about what types are allowed * `match` to ensure every case is handled - when enums won't work... trait objects → Ch17 - more about vectors → [API document](https://doc.rust-lang.org/std/vec/struct.Vec.html) ## 8.2. [Storing UTF-8 Encoded Text with Strings](https://doc.rust-lang.org/book/ch08-02-strings.html) - difficult? * Rust exposes possible errors * strings are complicated than we think * UTF-8 - string = a collection of bytes + some useful methods ### [What Is a String?](https://doc.rust-lang.org/book/ch08-02-strings.html#what-is-a-string) - “string” means... * string slice `str` (in the core language), and + usually in its borrowed form `&str` + UTF-8 encoded * the `String` type (in the standard library) + growable, mutable, owned + UTF-8 encoded * both, not just one of above - other string types ### [Creating a New String](https://doc.rust-lang.org/book/ch08-02-strings.html#creating-a-new-string) - like `Vec<T>` - an empty string ```rust let mut s = String::new(); ``` - with initial contents * `to_string` ```rust let s = "contents".to_string(); ``` * `from` ```rust let s = String::from("contents") ``` - UTF-8 ok! ### [Updating a String](https://doc.rust-lang.org/book/ch08-02-strings.html#updating-a-string) #### [Appending to a String with `push_str` and `push`](https://doc.rust-lang.org/book/ch08-02-strings.html#appending-to-a-string-with-push_str-and-push) - `push_str` -- append a string slice * we can't print `s2` if its ownership was taken - `push` -- append a single character #### [Concatenation with the `+` Operator or the `format!` Macro](https://doc.rust-lang.org/book/ch08-02-strings.html#concatenation-with-the--operator-or-the-format-macro) - `+` operator ```rust let s = s1 + &s2; ``` * use `add` (generic) method actually ```rust fn add(self, s: &str) -> String { ``` + `&s2` (`&String`) is *coerce*d into `&s2[..]` (`&str`) + `s1` is moved by `self` (can no longer be used) * becomes heavy to add multiple strings → use `format!` - `format!` macro ```rust let s = format!("{}-{}-{}", s1, s2, s3); ``` * like `println!` * doesn't take any ownership ### [Indexing into Strings](https://doc.rust-lang.org/book/ch08-02-strings.html#indexing-into-strings) Rust strings cannot be indexed #### [Internal Representation](https://doc.rust-lang.org/book/ch08-02-strings.html#internal-representation) because UTF-8 uses variable-width encoding #### [Bytes and Scalar Values and Grapheme Clusters! Oh My!](https://doc.rust-lang.org/book/ch08-02-strings.html#bytes-and-scalar-values-and-grapheme-clusters-oh-my) - 3 ways to look at “नमस्ते” (hello in [Devanagari](https://en.wikipedia.org/wiki/Devanagari)) * 18 bytes ``` [224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164, 224, 165, 135] ``` * 6 scalar values ``` ['न', 'म', 'स', '्', 'त', 'े'] ``` * 4 grapheme clusters (we would call *letter*s) ``` ["न", "म", "स्", "ते"] ``` - indexing should take constant time (O(1)) * but walking through is required (to tell how many characters) ### [Slicing Strings](https://doc.rust-lang.org/book/ch08-02-strings.html#slicing-strings) use ranges with caution -- crash if not a char boundary ### [Methods for Iterating Over Strings]() - `.bytes()` by bytes - `.chars()` by scalar values - by grapheme clusters? not provided by standard library ### [Strings Are Not So Simple](https://doc.rust-lang.org/book/ch08-02-strings.html#strings-are-not-so-simple) trade-off of complexity ## 8.3. [Storing Keys with Associated Values in Hash Maps](https://doc.rust-lang.org/book/ch08-03-hash-maps.html) - *hash map* -- `HashMap<K, V>` * map keys of type `K` to values of type `V` * via *hashing function* - useful to look up by a key of any type (rather than an index) - check [the document](https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html) for more information ### [Creating a New Hash Map](https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map) - with `new` and `insert` ```rust use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50); ``` * require to `use` (not often used enough to be included in prelude) * no build-in macro to construct (have less support) - with `zip` and `collect` ```rust use std::collections::HashMap; let keys = vec![String::from("Blue"), String::from("Yellow")]; let values = vec![10, 50]; let mut scores: HashMap<_, _> = keys.into_iter().zip(values.into_iter()).collect(); ``` * can `collect` into many types of data structures (can't infer) * underscores for Rust to infer the rest * 💬 `into_iter` vs `iter`? [stackoverflow](https://stackoverflow.com/questions/34733811) + `iter`: iterate by references + `iter_mut`: iterate by mutable references + `into_iter`: iterate and move items ### [Hash Maps and Ownership](https://doc.rust-lang.org/book/ch08-03-hash-maps.html#hash-maps-and-ownership) - `insert` keys and values * types with `Copy` trait (like `i32`): copied into * owned values (like `String`): moved into - `insert` references * must be valid as long as the hash map does → Ch10 ### [Accessing Values in a Hash Map](https://doc.rust-lang.org/book/ch08-03-hash-maps.html#accessing-values-in-a-hash-map) - `.get(&key)` → `Option<&V>` - iterate ```rust for (key, value) in &scores { println!("{}: {}", key, value); } ``` ### [Updating a Hash Map](https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-hash-map) - but each key can only have one value * replace old one * ignore new one * combine 2 values #### [Overwriting a Value](https://doc.rust-lang.org/book/ch08-03-hash-maps.html#overwriting-a-value) replace by `insert` (if already has the same key) #### [Only Inserting a Value If the Key Has No Value](https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value) `.entry(&key).or_insert(value)` (an `Entry` enum here) #### [Updating a Value Based on the Old Value](https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value) `.entry(&key).or_insert(value)` and update by the returned mutable reference (`&mut V`) ### [Hashing Functions](https://doc.rust-lang.org/book/ch08-03-hash-maps.html#hashing-functions) - “cryptographically strong” hashing function by default * secure but not fast - switch to your own *hasher* * `BuildHasher` trait → Ch10 * [crates.io](https://crates.io/) has many common hashing algorithms ### [Summary](https://doc.rust-lang.org/book/ch08-03-hash-maps.html#summary) - exercises * the mean, median and mode of a list * pig latin * employees and departments - read the document - next: error handling # 9. [Error Handling](https://doc.rust-lang.org/book/ch09-00-error-handling.html) - take action to errors, or compile failed → robust - 2 categories * `Result<T, E>` for recoverable * `panic!` for unrecoverable * no exception in Rust - recover, or stop execution? ## 9.1. [Unrecoverable Errors with `panic!`](https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html) - `panic!` macro * for things that you can do nothing about it * executes + print a failure message + unwind and clean up the stack + quit :::info ### [Unwinding the Stack or Aborting in Response to a Panic](https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html#unwinding-the-stack-or-aborting-in-response-to-a-panic) - in response to a panic * *unwinding* + clean up the stack (a lot of work) * *abort* + leave it cleaned by OS + smaller binary - in *Cargo.toml* ```rust [profile.release] panic = 'abort' ``` ::: - the error messages * the panic message * where the panic occurred + might be in other's code + use the backtrace to figure out... ### [Using a `panic!` Backtrace](https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html#using-a-panic-backtrace) - example: access the 100th element of a vector has only 3 elements * in C, undefined behavior (*buffer overread*) → security vulnerabilities * in Rust, stop execution - set `RUST_BACKTRACE` environment variable to get backtraces * from the top and read until the file you wrote * debug symbols must be enabled -- `cargo build`/`cargo run` without `--release` ## 9.2. [Recoverable Errors with `Result`](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html) - to do something about errors - `Result` enum ```rust enum Result<T, E> { Ok(T), Err(E), } ``` * generic type parameters for different situations - `File::open` returns what? * read the [document](https://doc.rust-lang.org/std/fs/struct.File.html#method.open) * ask the compiler -- give a wrong type annotation, then compile + `std::result::Result<std::fs::File, std::io::Error>` - use `match` to handle the `Result` * `Ok` and `Err` are already in prelude (like `Option`) ### [Matching on Different Errors](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#matching-on-different-errors) - create the file when it doesn't exist * `error.kind()` to get an `io::ErrorKind` (`match ErrorKind::NotFound`) * `File::create` could also fail (`match Err(error)`) - lots of `match`? `result.unwarp_or_else()` + closures → Ch13 * 💬 unwarp `Ok` for `T`, or else handle `Err` by `E` ### [Shortcuts for Panic on Error: `unwrap` and `expect`](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#shortcuts-for-panic-on-error-unwrap-and-expect) - helper methods of `Result` * `unwrap` + `Ok`: return the value + `Err`: `panic!` * `expect` + same as `unwarp` but use our error message ### [Propagating Errors](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#propagating-errors) - pass the error to the caller (may have more information) - example * return type: `Result<String, io::Error>` 💬 `io::Error` rely on implementation? * `File::open` + `Ok`: continue with `f` + `Err`: <code><del>panic!</del></code> `return Err(e)` * `f.read_to_string` + `Ok`: `Ok(s)` + `Err`: `Err(e)` + (ending expression) implicitly returned * let caller handle it #### [A Shortcut for Propagating Errors: the `?` Operator](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator) - almost the same as the `match` above * `Ok`: return value from expression * `Err`: return error from function * but go through `from` to convert error type * finally `Ok(s)` - shorten * chain method calls * `File::open("hello.txt")` #### [The `?` Operator Can Be Used in Functions That Return Result](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#the--operator-can-be-used-in-functions-that-return-result) - `?` operator is only allowed in a function returns... * `Result` * `Option` * `std::ops::Try` - main's return type must be * `()` * `Result<T, E>` -- `Result<(), Box<dyn Error>>` ## 9.3. [To `panic!` or Not to `panic!`](https://doc.rust-lang.org/book/ch09-03-to-panic-or-not-to-panic.html) a good default -- return `Result` and let caller decide it ### [Examples, Prototype Code, and Tests](https://doc.rust-lang.org/book/ch09-03-to-panic-or-not-to-panic.html#examples-prototype-code-and-tests) - use `unwrap` and `expect` * to illustrate the key point * as good placeholders when prototyping * to fail tests ### [Cases in Which You Have More Information Than the Compiler](https://doc.rust-lang.org/book/ch09-03-to-panic-or-not-to-panic.html#cases-in-which-you-have-more-information-than-the-compiler) - you know `Err` will not happen (such as hard-coding) ### [Guidelines for Error Handling](https://doc.rust-lang.org/book/ch09-03-to-panic-or-not-to-panic.html#guidelines-for-error-handling) - becoming in a bad state → `panic!` - bad states * assumption has been broken * not expected to happen occasionally * rely on not being in the bad state * not a good way to encode this - when you have no way to fix → `panic!` - when failure is expected → return a `Result` - `panic!` for safety reasons - annoying? use type system ### [Creating Custom Types for Validation](https://doc.rust-lang.org/book/ch09-03-to-panic-or-not-to-panic.html#creating-custom-types-for-validation) - check every time → not good - define a `Guess` type 💬 to encapsulation * `::new()` ensure the value is valid * `.value()` return the value (read-only) ### [Summary](https://doc.rust-lang.org/book/ch09-03-to-panic-or-not-to-panic.html#summary) - properly use... * `panic!` to stop instead of proceed * `Result` to tell caller it could recover - next: generics # 10. [Generic Types, Traits, and Lifetimes](https://doc.rust-lang.org/book/ch10-00-generics.html) - *generic*: express behavior without concrete types * such as `Option<T>`, `Vec<T>`, `HashMap<K, V>`, `Result<T, E>` * now define your own - in this chapter * reduce duplication (without/with *generic*) * *trait* * *lifetime* ### [Removing Duplication by Extracting a Function](https://doc.rust-lang.org/book/ch10-00-generics.html#removing-duplication-by-extracting-a-function) - remove duplication without generic first - example: largest number * have to update the code in multiple places * define a function to express the concept abstractly - steps 1. identify 2. extract 3. replace ## 10.1. [Generic Data Types](https://doc.rust-lang.org/book/ch10-01-syntax.html) define functions, structs, enums, methods using generics ### [In Function Definitions](https://doc.rust-lang.org/book/ch10-01-syntax.html#in-function-definitions) - example: `largest_i32`, `largest_char` → `largest<T>` * `T` for type (convention) * declare parameters and return type * compile error: `T` might need a bound for `std::cmp::PartialOrd` + trait ### [In Struct Definitions](https://doc.rust-lang.org/book/ch10-01-syntax.html#in-struct-definitions) - example ```rust struct Point<T> { x: T, y: T, } ``` * both `x` and `y` should be the same type `T` + otherwise, use multiple generic types + however, too many may hard to read ### [In Enum Definitions](https://doc.rust-lang.org/book/ch10-01-syntax.html#in-enum-definitions) - example: `Option<T>` - example: `Result<T, E>` ### [In Method Definitions](https://doc.rust-lang.org/book/ch10-01-syntax.html#in-method-definitions) - use existing types ```rust impl<T> Point<T> { fn x(&self) -> &T { … } } ``` - only for `Point<f32>` ```rust impl Point<f32> { fn distance(&self) -> &f32 { … } } ``` - use different types ```rust impl<T, U> Point<T, U> { fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> { … } } ``` ### [Performance of Code Using Generics](https://doc.rust-lang.org/book/ch10-01-syntax.html#performance-of-code-using-generics) *monomorphization* at compile time → no run-time cost ## 10.2 [Traits: Defining Shared Behavior](https://doc.rust-lang.org/book/ch10-02-traits.html) - *trait*: describe functionality between types * similar to *interface* ### [Defining a Trait](https://doc.rust-lang.org/book/ch10-02-traits.html#defining-a-trait) - to group required method signatures for some purpose - example ```rust pub trait Summary { fn summarize(&self) -> String; } ``` * each type with this trait must provide the implementation (compiler check) * can have multiple methods ### [Implementing a Trait on a Type](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) - similar to regular methods ```rust impl Summary for Tweet { fn summarize(&self) -> String { … } } ``` * thus `tweet.summarize()` can be called - restrictions * bring the trait into scope * while the trait should be `pub` * the trait or the type is local to our crate + *coherence* (*orphan rule*): implement the same trait for the same type only once ### [Default Implementations](https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations) - so as to keep/overwrite the default behavior - leave `impl Summary for NewArticle {}` empty (not to override) * `article.summarize()` can be called - having a default implementation doesn't change the one already override - can call other methods in the same trait * useful to customize just a small part ### [Traits as Parameters](https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters) - accept many different types ```rust pub fn notify(item: &impl Summary) { … } ``` #### [Trait Bound Syntax](https://doc.rust-lang.org/book/ch10-02-traits.html#trait-bound-syntax) - syntax sugar: longer but more verbose - force arguments be the same type ```rust pub fn notify<T: Summary>(item1: &T, item2: &T) { … } ``` #### [Specifying Multiple Trait Bounds with the `+` Syntax](https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax) ```rust pub fn notify(item: &(impl Summary + Display)) { … } ``` ```rust pub fn notify<T: Summary + Display>(item: &T) { … } ``` #### [Clearer Trait Bounds with `where` Clauses](https://doc.rust-lang.org/book/ch10-02-traits.html#clearer-trait-bounds-with-where-clauses) ```rust fn function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 { … } ``` ```rust fn function<T, U>(t: &t, u: &U) -> i32 where T: Display + Clone, U: Clone + Debug { ``` ### [Returning Types that Implement Traits](https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits) - for types that * only the compiler knows -- closures * very long to specify -- iterators - returning different types is not allowed → Ch17 ### [Fixing the `largest` Function with Trait Bounds](https://doc.rust-lang.org/book/ch10-02-traits.html#fixing-the-largest-function-with-trait-bounds) - to compare: `PartialOrd` - to copy: `Copy` * not to copy + use `Clone` (slow) + use references ### [Using Trait Bounds to Conditionally Implement Methods](https://doc.rust-lang.org/book/ch10-02-traits.html#using-trait-bounds-to-conditionally-implement-methods) - only `Display + PartialOrd` of `Pair<T>` has `cmp_display()` - *blanket*: implement a trait for any type that satisfies the trait bounds 💬 ? ## 10.3. [Validating References with Lifetimes](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html) - generic lifetime parameter: have to annotate *lifetime* in some cases * most of the time, *lifetime*s of references are implicit and inferred (just like types) ### [Preventing Dangling References with Lifetimes](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#preventing-dangling-references-with-lifetimes) borrow the value from the inner scope → won't compile: not live long enough ### [The Borrow Checker](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#the-borrow-checker) *borrow checker* checks lifetimes to ensure all borrows are valid ### [Generic Lifetimes in Functions](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#generic-lifetimes-in-functions) - a function borrows 2 values, then return one of them → won't compile * we don't know the lifetime of which will be taken * solution: generic lifetime parameters ### [Lifetime Annotation Syntax](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-annotation-syntax) - limit relationships between references that a function can accept ```rust &'a i32 ``` * no lifetime will be changed * relationship: one lifetime annotation by itself -- does not have much meaning ### [Lifetime Annotations in Function Signatures](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-annotations-in-function-signatures) - to fix the function by lifetime annotations ```rust fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { … } ``` * constraint: all the parameters and the return value -- have the same lifetime `'a` * `'a` is inferred as the smallest one that passed in * borrow checker disallows any possibility having invalid references ### [Thinking in Terms of Lifetimes](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#thinking-in-terms-of-lifetimes) - specify lifetime parameters depends on what your function is doing - if the lifetime parameter for the return type doesn't match the one of any parameter * thus, it must refer to a value within the function → won't compile ### [Lifetime Annotations in Struct Definitions](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-annotations-in-struct-definitions) add a lifetime annotation on every reference in the definition ```rust struct ImportantExcerpt<'a> { part: &'a str, } ``` ### [Lifetime Elision](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-elision) - history: functions without lifetime annotation * before (Rust pre-1.0): every reference do need an explicit lifetime * same predictable/deterministic patterns appear * let borrow checker infer it * more patterns may be found in the future - lifetimes of references * lifetimes of function/method parameters -- *input lifetime*s * lifetimes of return values -- *output lifetime*s - *litetime elision rules* 1. each reference parameter gets its own lifetime parameter 2. exactly one input lifetime parameter `'a` → all output lifetime parameters: `'a` 3. one of them is `&self` or `&mut self` → all output lifetime parameters: the lifetime of `self` - after all rules, still can't figure out the lifetime of any reference → error - example * exactly one * more then one, and not method ### [Lifetime Annotations in Method Definitions](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-annotations-in-method-definitions) - syntax: as generic type parameters ```rust impl<'a> ImportantExcerpt<'a> { fn announce_and_return_part(&self, announcement: &str) -> &str { … } } ``` * annotation of `&self` is not required -- 1st rule * annotation of return type is not required -- 3rd rule ### [The Static Lifetime](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#the-static-lifetime) - `'static` * all string literals have `'static` lifetime * usually not required ### [Generic Type Parameters, Trait Bounds, and Lifetimes Together](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#generic-type-parameters-trait-bounds-and-lifetimes-together) an overall example ### [Summary](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#summary) - review * generic type parameters * traits and trait bounds * lifetime annotations - more to learn → Ch17 {%hackmd @yipo/S1Hes0jTd %} {%hackmd @yipo/style %}