├── .gitignore ├── .prettierignore ├── LICENSE ├── Readme.md ├── en ├── assets │ ├── CNAME │ ├── custom3.js │ ├── header.jpg │ ├── header1.png │ ├── lang1.js │ ├── logo.png │ └── temp.md ├── book.toml ├── deploy.sh ├── src │ ├── .gitignore │ ├── SUMMARY.md │ ├── about.md │ ├── async │ │ ├── async-await.md │ │ ├── future.md │ │ ├── intro.md │ │ ├── pin-unpin.md │ │ └── stream.md │ ├── basic-types │ │ ├── char-bool-unit.md │ │ ├── functions.md │ │ ├── intro.md │ │ ├── numbers.md │ │ └── statements-expressions.md │ ├── circle-reference │ │ └── intro.md │ ├── collections │ │ ├── hashmap.md │ │ ├── intro.md │ │ ├── string.md │ │ └── vector.md │ ├── comments-docs.md │ ├── compound-types │ │ ├── array.md │ │ ├── enum.md │ │ ├── intro.md │ │ ├── slice.md │ │ ├── string.md │ │ ├── struct.md │ │ └── tuple.md │ ├── crate-module │ │ ├── crate.md │ │ ├── intro.md │ │ ├── module.md │ │ └── use-pub.md │ ├── elegant-code-base.md │ ├── errors.md │ ├── fight-compiler │ │ ├── borrowing.md │ │ └── intro.md │ ├── flow-control.md │ ├── formatted-output │ │ ├── debug-display.md │ │ ├── formatting.md │ │ ├── intro.md │ │ └── println.md │ ├── functional-programing │ │ ├── closure.md │ │ ├── intro.md │ │ └── iterator.md │ ├── generics-traits │ │ ├── advanced-traits.md │ │ ├── const-generics.md │ │ ├── generics.md │ │ ├── intro.md │ │ ├── trait-object.md │ │ └── traits.md │ ├── global-variables.md │ ├── lifetime │ │ ├── advance.md │ │ ├── basic.md │ │ ├── intro.md │ │ └── static.md │ ├── macro.md │ ├── method.md │ ├── newtype-sized.md │ ├── ownership │ │ ├── borrowing.md │ │ ├── intro.md │ │ └── ownership.md │ ├── pattern-match │ │ ├── intro.md │ │ ├── match-iflet.md │ │ └── patterns.md │ ├── result-panic │ │ ├── intro.md │ │ ├── panic.md │ │ └── result.md │ ├── self-referential.md │ ├── smart-pointers │ │ ├── box.md │ │ ├── cell-refcell.md │ │ ├── deref.md │ │ ├── drop.md │ │ ├── intro.md │ │ └── rc-arc.md │ ├── std │ │ ├── String.md │ │ └── intro.md │ ├── tests │ │ ├── assertions.md │ │ ├── benchmark.md │ │ ├── intro.md │ │ ├── unit-integration.md │ │ └── write-tests.md │ ├── threads │ │ ├── atomic.md │ │ ├── basic-using.md │ │ ├── intro.md │ │ ├── message-passing.md │ │ ├── send-sync.md │ │ └── sync.md │ ├── type-conversions │ │ ├── as.md │ │ ├── from-into.md │ │ ├── intro.md │ │ └── others.md │ ├── unsafe │ │ ├── inline-asm.md │ │ └── intro.md │ ├── variables.md │ ├── weak.md │ └── why-exercise.md └── theme │ ├── index1.hbs │ └── style1.css ├── practices ├── doc-comments │ ├── .gitignore │ ├── Cargo.toml │ ├── Readme.md │ └── src │ │ ├── compute.rs │ │ └── lib.rs └── hello-package │ ├── .gitignore │ ├── Cargo.toml │ ├── Readme.md │ └── src │ ├── back_of_house.rs │ ├── front_of_house │ ├── hosting.rs │ ├── mod.rs │ └── serving.rs │ ├── lib.rs │ └── main.rs ├── scripts └── link_solution ├── solutions ├── basic-types │ ├── char-bool.md │ ├── functions.md │ ├── numbers.md │ └── statements.md ├── collections │ ├── Hashmap.md │ ├── String.md │ └── Vector.md ├── compound-types │ ├── array.md │ ├── enum.md │ ├── slice.md │ ├── string.md │ ├── struct.md │ └── tuple.md ├── crate-module │ ├── crate.md │ ├── module.md │ └── use-pub.md ├── fight-compiler │ └── borrowing.md ├── flow-control.md ├── formatted-output │ ├── debug-display.md │ ├── formatting.md │ └── println.md ├── functional-programing │ ├── closure.md │ └── iterator.md ├── generics-traits │ ├── advanced-trait.md │ ├── const-generics.md │ ├── generics.md │ ├── trait-object.md │ └── traits.md ├── lifetime │ ├── advance.md │ ├── basic.md │ └── static.md ├── method.md ├── newtype-sized.md ├── ownership │ ├── borrowing.md │ └── ownership.md ├── pattern-match │ ├── match.md │ └── patterns.md ├── result-panic │ ├── panic.md │ └── result.md ├── type-conversions │ ├── as.md │ ├── from-into.md │ └── others.md └── variables.md └── zh-CN ├── assets ├── CNAME ├── custom3.js ├── lang1.js └── mini-redis │ ├── .github │ └── workflows │ │ └── ci.yml │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ ├── examples │ ├── chat.rs │ ├── hello_world.rs │ ├── pub.rs │ └── sub.rs │ ├── src │ ├── bin │ │ ├── cli.rs │ │ └── server.rs │ ├── blocking_client.rs │ ├── buffer.rs │ ├── client.rs │ ├── cmd │ │ ├── get.rs │ │ ├── mod.rs │ │ ├── publish.rs │ │ ├── set.rs │ │ ├── subscribe.rs │ │ └── unknown.rs │ ├── connection.rs │ ├── db.rs │ ├── frame.rs │ ├── lib.rs │ ├── parse.rs │ ├── server.rs │ └── shutdown.rs │ └── tests │ ├── buffer.rs │ ├── client.rs │ └── server.rs ├── book.toml ├── deploy.sh ├── src ├── SUMMARY.md ├── about.md ├── async │ ├── async-await.md │ ├── future.md │ ├── intro.md │ ├── pin-unpin.md │ └── stream.md ├── basic-types │ ├── char-bool-unit.md │ ├── functions.md │ ├── intro.md │ ├── numbers.md │ └── statements-expressions.md ├── circle-reference │ └── intro.md ├── collections │ ├── hashmap.md │ ├── intro.md │ ├── string.md │ └── vector.md ├── comments-docs.md ├── compound-types │ ├── array.md │ ├── enum.md │ ├── intro.md │ ├── slice.md │ ├── string.md │ ├── struct.md │ └── tuple.md ├── crate-module │ ├── crate.md │ ├── intro.md │ ├── module.md │ └── use-pub.md ├── elegant-code-base.md ├── errors.md ├── flow-control.md ├── formatted-output.md ├── functional-programing │ ├── closure.md │ ├── intro.md │ └── iterator.md ├── functional-programming │ ├── closure.md │ └── intro.md ├── generics-traits │ ├── advanced-traits.md │ ├── const-generics.md │ ├── generics.md │ ├── intro.md │ ├── trait-object.md │ └── traits.md ├── global-variables.md ├── lifetime │ ├── advance.md │ ├── basic.md │ ├── intro.md │ └── static.md ├── macro.md ├── method.md ├── newtype-sized.md ├── ownership │ ├── borrowing.md │ ├── intro.md │ └── ownership.md ├── pattern-match │ ├── intro.md │ ├── match-iflet.md │ └── patterns.md ├── result-panic │ ├── intro.md │ ├── panic.md │ └── result.md ├── self-referential.md ├── smart-pointers │ ├── box.md │ ├── cell-refcell.md │ ├── deref.md │ ├── drop.md │ ├── intro.md │ └── rc-arc.md ├── tests │ ├── assertions.md │ ├── benchmark.md │ ├── intro.md │ ├── unit-integration.md │ └── write-tests.md ├── threads │ ├── atomic.md │ ├── basic-using.md │ ├── intro.md │ ├── message-passing.md │ ├── send-sync.md │ └── sync.md ├── type-conversions │ ├── as.md │ ├── from-into.md │ ├── intro.md │ └── others.md ├── unsafe │ ├── inline-asm.md │ └── intro.md ├── variables.md ├── weak.md └── why-exercise.md └── theme ├── index1.hbs └── style1.css /.gitignore: -------------------------------------------------------------------------------- 1 | en/book 2 | zh-CN/book 3 | **/.DS_Store 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.md -------------------------------------------------------------------------------- /en/assets/CNAME: -------------------------------------------------------------------------------- 1 | practice.course.rs -------------------------------------------------------------------------------- /en/assets/header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunface/rust-by-practice/e0e88e963a3d2c96029e1e3dca3397d38db78cab/en/assets/header.jpg -------------------------------------------------------------------------------- /en/assets/header1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunface/rust-by-practice/e0e88e963a3d2c96029e1e3dca3397d38db78cab/en/assets/header1.png -------------------------------------------------------------------------------- /en/assets/lang1.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var path = window.location.pathname; 3 | var link = "https://practice.course.rs" + path; 4 | var word = "English"; 5 | var lang = "zh-CN"; 6 | var changeLang = "切换到英语"; 7 | 8 | if (window.location.href.indexOf("zh.") == -1) { 9 | link = "https://practice-zh.course.rs" + path; 10 | word = "简体中文"; 11 | lang = "en"; 12 | changeLang = "Switch to Chinese"; 13 | } 14 | 15 | var lang_node = ""; 16 | if (link != "") { 17 | lang_node = 18 | ' ' + 25 | word + 26 | ""; 27 | } 28 | 29 | console.log(lang_node); 30 | var insertNode = document.getElementsByClassName("right-buttons"); 31 | if (insertNode.length > 0) { 32 | var html = insertNode[0].innerHTML; 33 | insertNode[0].innerHTML = html + lang_node; 34 | } 35 | })(); 36 | -------------------------------------------------------------------------------- /en/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunface/rust-by-practice/e0e88e963a3d2c96029e1e3dca3397d38db78cab/en/assets/logo.png -------------------------------------------------------------------------------- /en/assets/temp.md: -------------------------------------------------------------------------------- 1 | # 字符、布尔、单元类型 2 | 3 | ### 字符 4 | 🌟 5 | ```rust 6 | 7 | use std::mem::size_of_val; 8 | fn main() { 9 | let c1 = 'a'; 10 | assert_eq!(size_of_val(&c1),1); 11 | 12 | let c2 = '中'; 13 | assert_eq!(size_of_val(&c2),3); 14 | } 15 | ``` 16 | 17 | 🌟 18 | ```rust 19 | 20 | fn main() { 21 | let c1 = "中"; 22 | print_char(c1); 23 | } 24 | 25 | fn print_char(c : char) { 26 | println!("{}", c); 27 | } 28 | ``` 29 | 30 | ### 布尔 31 | 🌟 32 | ```rust 33 | 34 | // 让 println! 工作 35 | fn main() { 36 | let _f: bool = false; 37 | 38 | let t = true; 39 | if !t { 40 | println!("hello, world"); 41 | } 42 | } 43 | ``` 44 | 45 | 🌟 46 | ```rust 47 | 48 | fn main() { 49 | let f = true; 50 | let t = true && false; 51 | assert_eq!(t, f); 52 | } 53 | ``` 54 | 55 | 56 | ### 单元类型 57 | 🌟🌟 58 | ```rust 59 | 60 | // 让代码工作,但不要修改 `implicitly_ret_unit` ! 61 | fn main() { 62 | let _v: () = (); 63 | 64 | let v = (2, 3); 65 | assert_eq!(v, implicitly_ret_unit()) 66 | } 67 | 68 | fn implicitly_ret_unit() { 69 | println!("I will returen a ()") 70 | } 71 | 72 | // 不要使用下面的函数,它只用于演示! 73 | fn explicitly_ret_unit() -> () { 74 | println!("I will returen a ()") 75 | } 76 | ``` 77 | 78 | 🌟🌟 单元类型占用的内存大小是多少? 79 | ```rust 80 | 81 | // 让代码工作:修改 `assert!` 中的 `4` 82 | use std::mem::size_of_val; 83 | fn main() { 84 | let unit: () = (); 85 | assert!(size_of_val(&unit) == 4); 86 | } 87 | ``` -------------------------------------------------------------------------------- /en/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Rust By Practice" 3 | description = "Learn Rust with Example, Exercise and real Practice, written with ❤️ by https://course.rs team" 4 | authors = ["sunface, https://im.dev"] 5 | language = "en" 6 | 7 | [output.html.playpen] 8 | editable = true 9 | editor = "ace" 10 | 11 | [output.html.fold] 12 | enable = true 13 | level = 1 14 | 15 | [output.html] 16 | additional-css = ["theme/style1.css"] 17 | additional-js = ["assets/custom3.js", "assets/lang1.js"] 18 | git-repository-url = "https://github.com/sunface/rust-by-practice" 19 | edit-url-template = "https://github.com/sunface/rust-by-practice/edit/master/en/{path}" 20 | 21 | [rust] 22 | edition = "2021" 23 | -------------------------------------------------------------------------------- /en/deploy.sh: -------------------------------------------------------------------------------- 1 | ## this script deploys the static website of course.rs to github pages 2 | 3 | ## build static website for book 4 | mdbook build 5 | ## copy CNAME info to book dir 6 | cp ./assets/CNAME ./book/ 7 | 8 | ## init git repo 9 | cd book 10 | git init 11 | git config user.name "sunface" 12 | git config user.email "cto@188.com" 13 | git add . 14 | git commit -m 'deploy' 15 | git branch -M gh-pages 16 | git remote add origin https://github.com/sunface/rust-by-practice 17 | 18 | ## push to github pages 19 | git push -u -f origin gh-pages -------------------------------------------------------------------------------- /en/src/.gitignore: -------------------------------------------------------------------------------- 1 | book -------------------------------------------------------------------------------- /en/src/about.md: -------------------------------------------------------------------------------- 1 | # Rust By Practice 2 | -------------------------------------------------------------------------------- /en/src/async/async-await.md: -------------------------------------------------------------------------------- 1 | # async and await! 2 | -------------------------------------------------------------------------------- /en/src/async/future.md: -------------------------------------------------------------------------------- 1 | # Future 2 | -------------------------------------------------------------------------------- /en/src/async/intro.md: -------------------------------------------------------------------------------- 1 | # Async/Await 2 | -------------------------------------------------------------------------------- /en/src/async/pin-unpin.md: -------------------------------------------------------------------------------- 1 | # Pin and Unpin 2 | -------------------------------------------------------------------------------- /en/src/async/stream.md: -------------------------------------------------------------------------------- 1 | # Stream 2 | -------------------------------------------------------------------------------- /en/src/basic-types/char-bool-unit.md: -------------------------------------------------------------------------------- 1 | # Char, Bool and Unit 2 | 3 | ### Char 4 | 1. 🌟 5 | ```rust,editable 6 | 7 | // Make it work 8 | use std::mem::size_of_val; 9 | fn main() { 10 | let c1 = 'a'; 11 | assert_eq!(size_of_val(&c1),1); 12 | 13 | let c2 = '中'; 14 | assert_eq!(size_of_val(&c2),3); 15 | 16 | println!("Success!"); 17 | } 18 | ``` 19 | 20 | 2. 🌟 21 | ```rust,editable 22 | 23 | // Make it work 24 | fn main() { 25 | let c1 = "中"; 26 | print_char(c1); 27 | } 28 | 29 | fn print_char(c : char) { 30 | println!("{}", c); 31 | } 32 | ``` 33 | 34 | ### Bool 35 | 3. 🌟 36 | ```rust,editable 37 | 38 | // Make println! work 39 | fn main() { 40 | let _f: bool = false; 41 | 42 | let t = true; 43 | if !t { 44 | println!("Success!"); 45 | } 46 | } 47 | ``` 48 | 49 | 4. 🌟 50 | ```rust,editable 51 | 52 | // Make it work 53 | fn main() { 54 | let f = true; 55 | let t = true && false; 56 | assert_eq!(t, f); 57 | 58 | println!("Success!"); 59 | } 60 | ``` 61 | 62 | 63 | ### Unit type 64 | 5. 🌟🌟 65 | ```rust,editable 66 | 67 | // Make it work, don't modify `implicitly_ret_unit` ! 68 | fn main() { 69 | let _v: () = (); 70 | 71 | let v = (2, 3); 72 | assert_eq!(v, implicitly_ret_unit()); 73 | 74 | println!("Success!"); 75 | } 76 | 77 | fn implicitly_ret_unit() { 78 | println!("I will return a ()"); 79 | } 80 | 81 | // Don't use this one 82 | fn explicitly_ret_unit() -> () { 83 | println!("I will return a ()"); 84 | } 85 | ``` 86 | 87 | 6. 🌟🌟 What's the size of the unit type? 88 | ```rust,editable 89 | 90 | // Modify `4` in assert to make it work 91 | use std::mem::size_of_val; 92 | fn main() { 93 | let unit: () = (); 94 | assert!(size_of_val(&unit) == 4); 95 | 96 | println!("Success!"); 97 | } 98 | ``` 99 | 100 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it 101 | -------------------------------------------------------------------------------- /en/src/basic-types/functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 1. 🌟🌟🌟 3 | ```rust,editable 4 | 5 | fn main() { 6 | // Don't modify the following two lines! 7 | let (x, y) = (1, 2); 8 | let s = sum(x, y); 9 | 10 | assert_eq!(s, 3); 11 | 12 | println!("Success!"); 13 | } 14 | 15 | fn sum(x, y: i32) { 16 | x + y; 17 | } 18 | ``` 19 | 20 | 21 | 2. 🌟 22 | ```rust,editable 23 | fn main() { 24 | print(); 25 | } 26 | 27 | // Replace i32 with another type 28 | fn print() -> i32 { 29 | println!("Success!"); 30 | } 31 | ``` 32 | 33 | 34 | 3. 🌟🌟🌟 35 | 36 | ```rust,editable 37 | // Solve it in two ways 38 | // DON'T let `println!` work 39 | fn main() { 40 | never_return(); 41 | 42 | println!("Failed!"); 43 | } 44 | 45 | fn never_return() -> ! { 46 | // Implement this function, don't modify the fn signatures 47 | 48 | } 49 | ``` 50 | 51 | ### Diverging functions 52 | Diverging functions never return to the caller, so they may be used in places where a value of any type is expected. 53 | 54 | 4. 🌟🌟 55 | ```rust,editable 56 | 57 | fn main() { 58 | println!("Success!"); 59 | } 60 | 61 | fn get_option(tp: u8) -> Option { 62 | match tp { 63 | 1 => { 64 | // TODO 65 | } 66 | _ => { 67 | // TODO 68 | } 69 | }; 70 | 71 | // Rather than returning a None, we use a diverging function instead 72 | never_return_fn() 73 | } 74 | 75 | // IMPLEMENT this function in THREE ways 76 | fn never_return_fn() -> ! { 77 | 78 | } 79 | ``` 80 | 81 | 5. 🌟🌟 82 | ```rust,editable 83 | 84 | fn main() { 85 | // FILL in the blank 86 | let b = __; 87 | 88 | let _v = match b { 89 | true => 1, 90 | // Diverging functions can also be used in match expression to replace a value of any value 91 | false => { 92 | println!("Success!"); 93 | panic!("we have no value for `false`, but we can panic"); 94 | } 95 | }; 96 | 97 | println!("Exercise Failed if printing out this line!"); 98 | } 99 | ``` 100 | 101 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice/blob/master/solutions/basic-types/functions.md)(under the solutions path), but only use it when you need it 102 | -------------------------------------------------------------------------------- /en/src/basic-types/intro.md: -------------------------------------------------------------------------------- 1 | # Basic Types 2 | Learning resources: 3 | - English: [Rust Book 3.2 and 3.3](https://doc.rust-lang.org/book/ch03-02-data-types.html) 4 | - 简体中文: [Rust语言圣经 - 基本类型](https://course.rs/basic/base-type/index.html) 5 | 6 | -------------------------------------------------------------------------------- /en/src/basic-types/statements-expressions.md: -------------------------------------------------------------------------------- 1 | # Statements and Expressions 2 | 3 | ### Examples 4 | ```rust,editable 5 | fn main() { 6 | let x = 5u32; 7 | 8 | let y = { 9 | let x_squared = x * x; 10 | let x_cube = x_squared * x; 11 | 12 | // This expression will be assigned to `y` 13 | x_cube + x_squared + x 14 | }; 15 | 16 | let z = { 17 | // The semicolon suppresses this expression and `()` is assigned to `z` 18 | 2 * x; 19 | }; 20 | 21 | println!("x is {:?}", x); 22 | println!("y is {:?}", y); 23 | println!("z is {:?}", z); 24 | } 25 | ``` 26 | 27 | ### Exercises 28 | 1. 🌟🌟 29 | ```rust,editable 30 | // Make it work with two ways 31 | fn main() { 32 | let v = { 33 | let mut x = 1; 34 | x += 2 35 | }; 36 | 37 | assert_eq!(v, 3); 38 | 39 | println!("Success!"); 40 | } 41 | ``` 42 | 43 | 2. 🌟 44 | ```rust,editable 45 | 46 | fn main() { 47 | let v = (let x = 3); 48 | 49 | assert!(v == 3); 50 | 51 | println!("Success!"); 52 | } 53 | ``` 54 | 55 | 3. 🌟 56 | ```rust,editable 57 | 58 | fn main() { 59 | let s = sum(1 , 2); 60 | assert_eq!(s, 3); 61 | 62 | println!("Success!"); 63 | } 64 | 65 | fn sum(x: i32, y: i32) -> i32 { 66 | x + y 67 | } 68 | ``` 69 | 70 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it 71 | -------------------------------------------------------------------------------- /en/src/circle-reference/intro.md: -------------------------------------------------------------------------------- 1 | # Circle reference and Self referential 2 | -------------------------------------------------------------------------------- /en/src/collections/intro.md: -------------------------------------------------------------------------------- 1 | # Collection Types 2 | Learning resources: 3 | - English: [Rust Book Chapter 8](https://doc.rust-lang.org/book/ch08-00-common-collections.html) 4 | - 简体中文: [Rust语言圣经 - 集合类型](https://course.rs/basic/collections/intro.html) 5 | 6 | -------------------------------------------------------------------------------- /en/src/compound-types/array.md: -------------------------------------------------------------------------------- 1 | # Array 2 | The type of array is `[T; Length]`, as you can see, array's length is part of their type signature. So their length must be known at compile time. 3 | 4 | For example, you cant initialize an array like below: 5 | ```rust 6 | fn init_arr(n: i32) { 7 | let arr = [1; n]; 8 | } 9 | ``` 10 | 11 | This will cause an error, because the compiler has no idea of the exact size of the array at compile time. 12 | 13 | 1. 🌟 14 | ```rust,editable 15 | 16 | fn main() { 17 | // Fill the blank with proper array type 18 | let arr: __ = [1, 2, 3, 4, 5]; 19 | 20 | // Modify the code below to make it work 21 | assert!(arr.len() == 4); 22 | 23 | println!("Success!"); 24 | } 25 | ``` 26 | 27 | 2. 🌟🌟 28 | ```rust,editable 29 | 30 | fn main() { 31 | // We can ignore parts of the array type or even the whole type, let the compiler infer it for us 32 | let arr0 = [1, 2, 3]; 33 | let arr: [_; 3] = ['a', 'b', 'c']; 34 | 35 | // Fill the blank 36 | // Arrays are stack allocated, `std::mem::size_of_val` returns the bytes which an array occupies 37 | // A char takes 4 bytes in Rust: Unicode char 38 | assert!(std::mem::size_of_val(&arr) == __); 39 | 40 | println!("Success!"); 41 | } 42 | ``` 43 | 44 | 3. 🌟 All elements in an array can be initialized to the same value at once. 45 | 46 | ```rust,editable 47 | 48 | fn main() { 49 | // Fill the blank 50 | let list: [i32; 100] = __ ; 51 | 52 | assert!(list[0] == 1); 53 | assert!(list.len() == 100); 54 | 55 | println!("Success!"); 56 | } 57 | ``` 58 | 59 | 4. 🌟 All elements in an array must be of the same type 60 | ```rust,editable 61 | 62 | fn main() { 63 | // Fix the error 64 | let _arr = [1, 2, '3']; 65 | 66 | println!("Success!"); 67 | } 68 | ``` 69 | 70 | 5. 🌟 Indexing starts at 0. 71 | ```rust,editable 72 | 73 | fn main() { 74 | let arr = ['a', 'b', 'c']; 75 | 76 | let ele = arr[1]; // Only modify this line to make the code work! 77 | 78 | assert!(ele == 'a'); 79 | 80 | println!("Success!"); 81 | } 82 | ``` 83 | 84 | 6. 🌟 Out of bounds indexing causes `panic`. 85 | ```rust,editable 86 | 87 | // Fix the error 88 | fn main() { 89 | let names = [String::from("Sunfei"), "Sunface".to_string()]; 90 | 91 | // `Get` returns an Option<&T>, it's safe to use 92 | let name0 = names.get(0).unwrap(); 93 | 94 | // But indexing is not safe 95 | let _name1 = &names[2]; 96 | 97 | println!("Success!"); 98 | } 99 | 100 | ``` 101 | 102 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice/blob/master/solutions/compound-types/array.md)(under the solutions path), but only use it when you need it 103 | -------------------------------------------------------------------------------- /en/src/compound-types/intro.md: -------------------------------------------------------------------------------- 1 | # Compound Types 2 | Learning resources: 3 | - English: [Rust Book 4.3, 5.1, 6.1, 8.2](https://doc.rust-lang.org/book/ch04-03-slices.html) 4 | - 简体中文: [Rust语言圣经 - 复合类型](https://course.rs/basic/compound-type/intro.html) 5 | 6 | 7 | -------------------------------------------------------------------------------- /en/src/compound-types/slice.md: -------------------------------------------------------------------------------- 1 | # Slice 2 | Slices are similar to arrays, but their length is not known at compile time, so you can't use slice directly. 3 | 4 | 1. 🌟🌟 Here, both `[i32]` and `str` are slice types, but directly using it will cause errors. You have to use the reference of the slice instead: `&[i32]`, `&str`. 5 | ```rust,editable 6 | 7 | // Fix the errors, DON'T add new lines! 8 | fn main() { 9 | let arr = [1, 2, 3]; 10 | let s1: [i32] = arr[0..2]; 11 | 12 | let s2: str = "hello, world" as str; 13 | 14 | println!("Success!"); 15 | } 16 | ``` 17 | 18 | A slice reference is a two-word object, for simplicity reasons, from now on we will use slice instead of `slice reference`. The first word is a pointer to the data, and the second word is the length of the slice. The word size is the same as usize, determined by the processor architecture, e.g. 64 bits on an x86-64. Slices can be used to borrow a section of an array, and have the type signature `&[T]`. 19 | 20 | 2. 🌟🌟🌟 21 | ```rust,editable 22 | 23 | fn main() { 24 | let arr: [char; 3] = ['中', '国', '人']; 25 | 26 | let slice = &arr[..2]; 27 | 28 | // Modify '8' to make it work 29 | // TIPS: slice( reference ) IS NOT an array, if it is an array, then `assert!` will be passed: Each of the two chars '中' and '国' occupies 4 bytes, 2 * 4 = 8 30 | assert!(std::mem::size_of_val(&slice) == 8); 31 | 32 | println!("Success!"); 33 | } 34 | ``` 35 | 36 | 3. 🌟🌟 37 | ```rust,editable 38 | 39 | fn main() { 40 | let arr: [i32; 5] = [1, 2, 3, 4, 5]; 41 | // Fill the blanks to make the code work 42 | let slice: __ = __; 43 | assert_eq!(slice, &[2, 3, 4]); 44 | 45 | println!("Success!"); 46 | } 47 | ``` 48 | 49 | ### String slices 50 | 4. 🌟 51 | ```rust,editable 52 | 53 | fn main() { 54 | let s = String::from("hello"); 55 | 56 | let slice1 = &s[0..2]; 57 | // Fill the blank to make the code work, DON'T USE 0..2 again 58 | let slice2 = &s[__]; 59 | 60 | assert_eq!(slice1, slice2); 61 | 62 | println!("Success!"); 63 | } 64 | ``` 65 | 66 | 5. 🌟 67 | ```rust,editable 68 | 69 | fn main() { 70 | let s = "你好,世界"; 71 | // Modify this line to make the code work 72 | let slice = &s[0..2]; 73 | 74 | assert!(slice == "你"); 75 | 76 | println!("Success!"); 77 | } 78 | ``` 79 | 80 | 6. 🌟🌟 `&String` can be implicitly converted into `&str`. 81 | ```rust,editable 82 | 83 | // Fix errors 84 | fn main() { 85 | let mut s = String::from("hello world"); 86 | 87 | // Here, &s is `&String` type, but `first_letter` needs a `&str` type. 88 | // It works because `&String` can be implicitly converted to `&str. If you want to know more, this is called `Deref coercion`. 89 | let letter = first_letter(&s); 90 | 91 | s.clear(); // error! 92 | 93 | println!("the first letter is: {}", letter); 94 | } 95 | fn first_letter(s: &str) -> &str { 96 | &s[..1] 97 | } 98 | ``` 99 | 100 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice/blob/master/solutions/compound-types/slice.md)(under the solutions path), but only use it when you need it 101 | -------------------------------------------------------------------------------- /en/src/compound-types/tuple.md: -------------------------------------------------------------------------------- 1 | # Tuple 2 | 1. 🌟 Elements in a tuple can have different types. Tuple's type signature is `(T1, T2, ...)`, where `T1`, `T2` are the types of tuple's members. 3 | ```rust,editable 4 | 5 | fn main() { 6 | let _t0: (u8,i16) = (0, -1); 7 | // Tuples can be tuple's members 8 | let _t1: (u8, (i16, u32)) = (0, (-1, 1)); 9 | // Fill the blanks to make the code work 10 | let t: (u8, __, i64, __, __) = (1u8, 2u16, 3i64, "hello", String::from(", world")); 11 | 12 | println!("Success!"); 13 | } 14 | ``` 15 | 16 | 2. 🌟 Members can be extracted from the tuple using indexing. 17 | ```rust,editable 18 | 19 | // Make it work 20 | fn main() { 21 | let t = ("i", "am", "sunface"); 22 | assert_eq!(t.1, "sunface"); 23 | 24 | println!("Success!"); 25 | } 26 | ``` 27 | 28 | 3. 🌟 Long tuples cannot be printed 29 | ```rust,editable 30 | 31 | // Fix the error 32 | fn main() { 33 | let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); 34 | println!("too long tuple: {:?}", too_long_tuple); 35 | } 36 | ``` 37 | 38 | 4. 🌟 Destructuring tuple with pattern. 39 | ```rust,editable 40 | 41 | fn main() { 42 | let tup = (1, 6.4, "hello"); 43 | 44 | // Fill the blank to make the code work 45 | let __ = tup; 46 | 47 | assert_eq!(x, 1); 48 | assert_eq!(y, "hello"); 49 | assert_eq!(z, 6.4); 50 | 51 | println!("Success!"); 52 | } 53 | ``` 54 | 55 | 5. 🌟🌟 Destructure assignments. 56 | ```rust,editable 57 | fn main() { 58 | let (x, y, z); 59 | 60 | // Fill the blank 61 | __ = (1, 2, 3); 62 | 63 | assert_eq!(x, 3); 64 | assert_eq!(y, 1); 65 | assert_eq!(z, 2); 66 | 67 | println!("Success!"); 68 | } 69 | ``` 70 | 71 | 6. 🌟🌟 Tuples can be used as function arguments and return values 72 | ```rust,editable 73 | 74 | fn main() { 75 | // Fill the blank, need a few computations here. 76 | let (x, y) = sum_multiply(__); 77 | 78 | assert_eq!(x, 5); 79 | assert_eq!(y, 6); 80 | 81 | println!("Success!"); 82 | } 83 | 84 | fn sum_multiply(nums: (i32, i32)) -> (i32, i32) { 85 | (nums.0 + nums.1, nums.0 * nums.1) 86 | } 87 | ``` 88 | 89 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice/blob/master/solutions/compound-types/tuple.md)(under the solutions path), but only use it when you need it -------------------------------------------------------------------------------- /en/src/crate-module/crate.md: -------------------------------------------------------------------------------- 1 | # Package and Crate 2 | A package is a project which you create with Cargo (in most cases), so it contains a `Cargo.toml` file in it. 3 | 4 | 1. 🌟 Create a package with below layout: 5 | ```shell 6 | . 7 | ├── Cargo.toml 8 | └── src 9 | └── main.rs 10 | 11 | 1 directory, 2 files 12 | ``` 13 | 14 | ```toml 15 | # in Cargo.toml 16 | [package] 17 | name = "hello-package" 18 | version = "0.1.0" 19 | edition = "2021" 20 | ``` 21 | 22 | > Note! We will use this package across the whole chapter as a practice project. 23 | 24 | 2. 🌟 Create a package with below layout: 25 | ```shell 26 | . 27 | ├── Cargo.toml 28 | └── src 29 | └── lib.rs 30 | 31 | 1 directory, 2 files 32 | ``` 33 | 34 | ```toml 35 | # in Cargo.toml 36 | [package] 37 | name = "hello-package1" 38 | version = "0.1.0" 39 | edition = "2021" 40 | ``` 41 | 42 | > Note! This package could be safely removed due to the first one's existence. 43 | 44 | 3. 🌟 45 | ```rust,editable 46 | /* FILL in the blank with your ANSWER */ 47 | 48 | // Q: What's the difference between package number 1 and number 2? 49 | // A: __ 50 | ``` 51 | 52 | 53 | ## Crate 54 | A crate is a binary or library. The crate root is a source file that the Rust compiler starts from and makes up the root module of the crate. 55 | 56 | In package `hello-package`, there is binary crate with the same name as the package : `hello-package`, and `src/main.rs` is the crate root of this binary crate. 57 | 58 | Similar to `hello-package`, `hello-package1` also has a crate in it, however, this package doesn't contain a binary crate but a library crate, and `src/lib.rs` is the crate root. 59 | 60 | 4. 🌟 61 | ```rust,editable 62 | /* FILL in the blank with your ANSWER */ 63 | 64 | // Q: What's the name of the library crate in package `hello-package1`? 65 | // A: __ 66 | ``` 67 | 68 | 69 | 5. 🌟🌟 Add a library crate for `hello-package` and describe it's files tree below: 70 | ```shell,editable 71 | # FILL in the blanks 72 | . 73 | ├── Cargo.lock 74 | ├── Cargo.toml 75 | ├── src 76 | │   ├── __ 77 | │   └── __ 78 | ``` 79 | 80 | After this step, there should be two crates in package `hello-package`: **a binary crate and a library crate, both with the same name as the package**. 81 | 82 | 6. 🌟🌟🌟 A package can contain at most one library crate, but it can contain as many binary crates as you would like by placing files in `src/bin` directory: **each file will be a separate binary crate with the same name as the file**. 83 | 84 | ```shell,editable 85 | # Create a package which contains 86 | # 1. three binary crates: `hello-package`, `main1` and `main2` 87 | # 2. one library crate 88 | # describe the directory tree below 89 | . 90 | ├── Cargo.toml 91 | ├── Cargo.lock 92 | ├── src 93 | │ ├── __ 94 | │ ├── __ 95 | │ └── __ 96 | │ └── __ 97 | │ └── __ 98 | ├── tests # directory for integrated tests files 99 | │ └── some_integration_tests.rs 100 | ├── benches # dir for benchmark files 101 | │ └── simple_bench.rs 102 | └── examples # dir for example files 103 | └── simple_example.rs 104 | ``` 105 | 106 | Yep, as you can see, the above package structure is very standard and is widely used in many Rust projects. 107 | 108 | 109 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice/blob/master/solutions/crate-module/crate.md) (under the solutions path), but only use it when you need it :) -------------------------------------------------------------------------------- /en/src/crate-module/intro.md: -------------------------------------------------------------------------------- 1 | # Crate and module 2 | Learning resources: 3 | - English: [Rust Book Chapter 7](https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html) 4 | - 简体中文: [Rust语言圣经 - 包和模块](https://course.rs/basic/crate-module/intro.html) 5 | 6 | -------------------------------------------------------------------------------- /en/src/crate-module/use-pub.md: -------------------------------------------------------------------------------- 1 | # Use and pub 2 | 1. 🌟 We can bring two types of the same name into the same scope with use, but you need `as` keyword. 3 | 4 | ```rust,editable 5 | use std::fmt::Result; 6 | use std::io::Result; 7 | 8 | fn main() {} 9 | ``` 10 | 11 | 2. 🌟🌟 If we are using multiple items defined in the same crate or module, then listing each item on its own line will take up too much vertical space. 12 | 13 | ```rust,editable 14 | 15 | // FILL in the blank in two ways 16 | // DON'T add new code line 17 | use std::collections::__; 18 | 19 | fn main() { 20 | let _c1:HashMap<&str, i32> = HashMap::new(); 21 | let mut c2 = BTreeMap::new(); 22 | c2.insert(1, "a"); 23 | let _c3: HashSet = HashSet::new(); 24 | } 25 | ``` 26 | 27 | ### Re-exporting names with `pub use` 28 | 3. 🌟🌟🌟 In our recently created package `hello-package`, add something to make the below code work 29 | ```rust,editable 30 | fn main() { 31 | assert_eq!(hello_package::hosting::seat_at_table(), "sit down please"); 32 | assert_eq!(hello_package::eat_at_restaurant(),"yummy yummy!"); 33 | } 34 | ``` 35 | 36 | 37 | ### Pub(in Crate) 38 | Sometimes we want an item only be public to a certain crate. For this we can use the `pub(in Crate)` syntax. 39 | 40 | #### Example 41 | ```rust,editable 42 | pub mod a { 43 | pub const I: i32 = 3; 44 | 45 | fn semisecret(x: i32) -> i32 { 46 | use self::b::c::J; 47 | x + J 48 | } 49 | 50 | pub fn bar(z: i32) -> i32 { 51 | semisecret(I) * z 52 | } 53 | pub fn foo(y: i32) -> i32 { 54 | semisecret(I) + y 55 | } 56 | 57 | mod b { 58 | pub(in crate::a) mod c { 59 | pub(in crate::a) const J: i32 = 4; 60 | } 61 | } 62 | } 63 | ``` 64 | 65 | ### Full Code 66 | The full code of `hello-package` is [here](https://github.com/sunface/rust-by-practice/tree/master/practices/hello-package). 67 | 68 | 69 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice/blob/master/solutions/crate-module/use-pub.md) (under the solutions path), but only use it when you need it :) -------------------------------------------------------------------------------- /en/src/elegant-code-base.md: -------------------------------------------------------------------------------- 1 | # Small projects with Elegant code base 2 | Following questions come up weekly in online Rust discussions: 3 | 4 | - I just finished reading The Book, what should I do next ? 5 | - What projects would you recommend to a Rust beginner? 6 | - Looking for small projects with an elegant code base 7 | - Codes that is easy to read and learn 8 | 9 | The answers to these questions are always **Practice**: doing some exercises, and then reading some small and excellent Rust projects. 10 | 11 | This is precisely the goal of this book, so, collecting relative resourses and representing in _Rust By Practice_ seems not a bad idea. 12 | 13 | ### 1. Ripgrep 14 | 15 | Answers for above questions usually came with [`ripgrep`](https://github.com/BurntSushi/ripgrep), though I don't think it is a **small** project, but yes, go for it if you are not afraid to delve deep a bit. 16 | 17 | ### 2. Building a text editor 18 | Tutorial [`https://www.flenker.blog/hecto/`](https://www.flenker.blog/hecto/) will lead you to build a text editor from scratch. 19 | 20 | ### 3. Ncspot 21 | [Ncspot](https://github.com/hrkfdn/ncspot), a terminal Spotify client. Small, simple, well organized and async, it's good for learning. 22 | 23 | ### 4. Command Line Rust 24 | [This project](https://github.com/kyclark/command-line-rust) is for the book `Command-Line Rust(O'Reily)`, it will show you how to write small CLIs (clones of `head`, `cat`, `ls`). 25 | 26 | ### 5. pngme book 27 | [This book](https://jrdngr.github.io/pngme_book/) will guide you to make a command line program that lets you hide secret messages in PNG files. The primary goal here is to get you writing code. The secondary goal is to get you reading documentation. 28 | 29 | ### 6. Writing an OS in Rust 30 | 31 | [This blog series](https://os.phil-opp.com) creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code, so you can follow along if you like. The source code is also available in the corresponding [Github repository](https://github.com/phil-opp/blog_os). 32 | 33 | 34 | ### 7. CodeCrafters.io: Build your own Git, Docker, SQLite, or Redis 35 | 36 | On [CodeCrafters](https://codecrafters.io/for/rust), you can recreate your favorite developer tools from scratch. It's a hands-on, minimally-guided approach to master Rust, while appreciating the internals and documentation of popular technology that we use every day. 37 | 38 | ### 8. mini-redis 39 | [mini-redis](https://github.com/tokio-rs/mini-redis) is an incomplete Redis client and server implementation using tokio, it has decent code base and detail explanations, very suitable for learning Rust and asynchronous programming. 40 | 41 | ### 9. Writing Interpreters in Rust 42 | 43 | [This online book](https://rust-hosted-langs.github.io/book/) will walk through the basics of interpreted language implementation in Rust with a focus on the challenges that are specific to using Rust. 44 | 45 | --- 46 | 47 | 48 | **To be continued...** 49 | -------------------------------------------------------------------------------- /en/src/errors.md: -------------------------------------------------------------------------------- 1 | # Errors 2 | -------------------------------------------------------------------------------- /en/src/fight-compiler/borrowing.md: -------------------------------------------------------------------------------- 1 | # Borrowing 2 | 3 | 1. 🌟🌟 4 | ```rust,editable 5 | // FIX the error without removing any code line 6 | struct test { 7 | list: Vec, 8 | a: i32 9 | } 10 | 11 | impl test { 12 | pub fn new() -> Self { 13 | test { list:vec![1,2,3,4,5,6,7], a:0 } 14 | } 15 | 16 | pub fn run(&mut self) { 17 | for i in self.list.iter() { 18 | self.do_something(*i) 19 | } 20 | 21 | } 22 | 23 | pub fn do_something(&mut self, n: i32) { 24 | self.a = n; 25 | } 26 | } 27 | 28 | fn main() {} 29 | ``` 30 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice/blob/master/solutions/fight-compiler/borrowing.md)(under the solutions path), but only use it when you need it :) 31 | -------------------------------------------------------------------------------- /en/src/fight-compiler/intro.md: -------------------------------------------------------------------------------- 1 | # Fighting with Compiler 2 | Fighting with compiler is very common in our daily coding, especially for those unfamiliar with Rust. 3 | 4 | This chapter will provide some exercises to help us avoid such cases to lower the steep learning curve. -------------------------------------------------------------------------------- /en/src/formatted-output/intro.md: -------------------------------------------------------------------------------- 1 | # Formatted output 2 | 3 | ```rust,editable,ignore,mdbook-runnable 4 | fn main() { 5 | // In general, the `{}` will be automatically replaced with any 6 | // arguments. These will be stringified. 7 | println!("{} days", 31); 8 | 9 | // Without a suffix, 31 becomes an i32. You can change what type 31 is 10 | // by providing a suffix. The number 31i64 for example has the type i64. 11 | 12 | // There are various optional patterns this works with. Positional 13 | // arguments can be used. 14 | println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob"); 15 | 16 | // As can named arguments. 17 | println!("{subject} {verb} {object}", 18 | object="the lazy dog", 19 | subject="the quick brown fox", 20 | verb="jumps over"); 21 | 22 | // Special formatting can be specified after a `:`. 23 | println!("{} of {:b} people know binary, the other half doesn't", 1, 2); 24 | 25 | // You can right-align text with a specified width. This will output 26 | // " 1". 5 white spaces and a "1". 27 | println!("{number:>width$}", number=1, width=6); 28 | 29 | // You can pad numbers with extra zeroes. This will output "000001". 30 | println!("{number:0>width$}", number=1, width=6); 31 | 32 | // Rust even checks to make sure the correct number of arguments are 33 | // used. 34 | println!("My name is {0}, {1} {0}", "Bond"); 35 | // FIXME ^ Add the missing argument: "James" 36 | 37 | // Create a structure named `Structure` which contains an `i32`. 38 | #[allow(dead_code)] 39 | struct Structure(i32); 40 | 41 | // However, custom types such as this structure require more complicated 42 | // handling. This will not work. 43 | println!("This struct `{}` won't print...", Structure(3)); 44 | // FIXME ^ Comment out this line. 45 | 46 | // For Rust 1.58 and above, you can directly capture the argument from 47 | // surrounding variable. Just like the above, this will output 48 | // " 1". 5 white spaces and a "1". 49 | let number: f64 = 1.0; 50 | let width: usize = 6; 51 | println!("{number:>width$}"); 52 | } 53 | ``` 54 | 55 | [`std::fmt`][fmt] contains many [`traits`][traits] which govern the display 56 | of text. The base form of two important ones are listed below: 57 | 58 | * `fmt::Debug`: Uses the `{:?}` marker. Format text for debugging purposes. 59 | * `fmt::Display`: Uses the `{}` marker. Format text in a more elegant, user 60 | friendly fashion. 61 | 62 | Here, we used `fmt::Display` because the std library provides implementations 63 | for these types. To print text for custom types, more steps are required. 64 | 65 | Implementing the `fmt::Display` trait automatically implements the 66 | [`ToString`] trait which allows us to [convert] the type to [`String`][string]. 67 | -------------------------------------------------------------------------------- /en/src/formatted-output/println.md: -------------------------------------------------------------------------------- 1 | # println! and format! 2 | Printing is handled by a series of [`macros`][macros] defined in [`std::fmt`][fmt] 3 | Some of which include: 4 | 5 | * `format!`: write formatted text to [`String`][string] 6 | * `print!`: same as `format!` but the text is printed to the console (io::stdout). 7 | * `println!`: same as `print!` but a newline is appended. 8 | * `eprint!`: same as `format!` but the text is printed to the standard error (io::stderr). 9 | * `eprintln!`: same as `eprint!`but a newline is appended. 10 | 11 | All parse text in the same fashion. As a plus, Rust checks format correctness at compile time. 12 | 13 | ## `format!` 14 | 1.🌟 15 | ```rust,editable 16 | 17 | fn main() { 18 | let s1 = "hello"; 19 | /* Fill in the blank */ 20 | let s = format!(__); 21 | assert_eq!(s, "hello, world!"); 22 | } 23 | ``` 24 | 25 | ## `print!`, `println!` 26 | 2.🌟 27 | ```rust,editable 28 | 29 | fn main() { 30 | /* Fill in the blanks to make it print: 31 | Hello world, I am 32 | Sunface! 33 | */ 34 | __("hello world, "); 35 | __("I am"); 36 | __("Sunface!"); 37 | } 38 | ``` 39 | 40 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice/blob/master/solutions/formatted-output/println.md)(under the solutions path), but only use it when you need it :) 41 | -------------------------------------------------------------------------------- /en/src/functional-programing/intro.md: -------------------------------------------------------------------------------- 1 | # Functional programing 2 | Learning resources: 3 | - English: [Rust Book 13](https://doc.rust-lang.org/book/ch13-00-functional-features.html) 4 | - 简体中文: [Rust语言圣经 - 函数式编程:闭包和迭代器](https://course.rs/advance/functional-programing/intro.html) 5 | 6 | -------------------------------------------------------------------------------- /en/src/generics-traits/generics.md: -------------------------------------------------------------------------------- 1 | # Generics 2 | 3 | ### Functions 4 | 1. 🌟🌟🌟 5 | ```rust,editable 6 | 7 | // Fill in the blanks to make it work 8 | struct A; // Concrete type `A`. 9 | struct S(A); // Concrete type `S`. 10 | struct SGen(T); // Generic type `SGen`. 11 | 12 | fn reg_fn(_s: S) {} 13 | 14 | fn gen_spec_t(_s: SGen) {} 15 | 16 | fn gen_spec_i32(_s: SGen) {} 17 | 18 | fn generic(_s: SGen) {} 19 | 20 | fn main() { 21 | // Using the non-generic functions 22 | reg_fn(__); // Concrete type. 23 | gen_spec_t(__); // Implicitly specified type parameter `A`. 24 | gen_spec_i32(__); // Implicitly specified type parameter `i32`. 25 | 26 | // Explicitly specified type parameter `char` to `generic()`. 27 | generic::(__); 28 | 29 | // Implicitly specified type parameter `char` to `generic()`. 30 | generic(__); 31 | 32 | println!("Success!"); 33 | } 34 | ``` 35 | 36 | 2. 🌟🌟 A function call with explicitly specified type parameters looks like: `fun::()`. 37 | ```rust,editable 38 | 39 | // Implement the generic function below. 40 | fn sum 41 | 42 | fn main() { 43 | assert_eq!(5, sum(2i8, 3i8)); 44 | assert_eq!(50, sum(20, 30)); 45 | assert_eq!(2.46, sum(1.23, 1.23)); 46 | 47 | println!("Success!"); 48 | } 49 | ``` 50 | 51 | 52 | ### Struct and `impl` 53 | 54 | 3. 🌟 55 | ```rust,editable 56 | 57 | // Implement struct Point to make it work. 58 | 59 | 60 | fn main() { 61 | let integer = Point { x: 5, y: 10 }; 62 | let float = Point { x: 1.0, y: 4.0 }; 63 | 64 | println!("Success!"); 65 | } 66 | ``` 67 | 68 | 4. 🌟🌟 69 | ```rust,editable 70 | 71 | // Modify this struct to make the code work 72 | struct Point { 73 | x: T, 74 | y: T, 75 | } 76 | 77 | fn main() { 78 | // DON'T modify this code. 79 | let p = Point{x: 5, y : "hello".to_string()}; 80 | 81 | println!("Success!"); 82 | } 83 | ``` 84 | 85 | 5. 🌟🌟 86 | ```rust,editable 87 | 88 | // Add generic for Val to make the code work, DON'T modify the code in `main`. 89 | struct Val { 90 | val: f64, 91 | } 92 | 93 | impl Val { 94 | fn value(&self) -> &f64 { 95 | &self.val 96 | } 97 | } 98 | 99 | 100 | fn main() { 101 | let x = Val{ val: 3.0 }; 102 | let y = Val{ val: "hello".to_string()}; 103 | println!("{}, {}", x.value(), y.value()); 104 | } 105 | ``` 106 | 107 | ### Method 108 | 6. 🌟🌟🌟 109 | 110 | ```rust,editable 111 | struct Point { 112 | x: T, 113 | y: U, 114 | } 115 | 116 | impl Point { 117 | // Implement mixup to make it work, DON'T modify other code. 118 | fn mixup 119 | } 120 | 121 | fn main() { 122 | let p1 = Point { x: 5, y: 10 }; 123 | let p2 = Point { x: "Hello", y: '中'}; 124 | 125 | let p3 = p1.mixup(p2); 126 | 127 | assert_eq!(p3.x, 5); 128 | assert_eq!(p3.y, '中'); 129 | 130 | println!("Success!"); 131 | } 132 | ``` 133 | 134 | 7. 🌟🌟 135 | ```rust,editable 136 | 137 | // Fix the errors to make the code work. 138 | struct Point { 139 | x: T, 140 | y: T, 141 | } 142 | 143 | impl Point { 144 | fn distance_from_origin(&self) -> f32 { 145 | (self.x.powi(2) + self.y.powi(2)).sqrt() 146 | } 147 | } 148 | 149 | fn main() { 150 | let p = Point{x: 5, y: 10}; 151 | println!("{}",p.distance_from_origin()); 152 | } 153 | ``` 154 | 155 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice/blob/master/solutions/generics-traits/generics.md)(under the solutions path), but only use it when you need it 156 | 157 | -------------------------------------------------------------------------------- /en/src/generics-traits/intro.md: -------------------------------------------------------------------------------- 1 | # Generics and Traits 2 | Learning resources: 3 | - English: [Rust Book 10.1, 10.2](https://doc.rust-lang.org/book/ch10-00-generics.html) 4 | - 简体中文: [Rust语言圣经 - 模式匹配](https://course.rs/basic/trait/intro.html) 5 | 6 | 7 | -------------------------------------------------------------------------------- /en/src/global-variables.md: -------------------------------------------------------------------------------- 1 | # Global variables 2 | -------------------------------------------------------------------------------- /en/src/lifetime/intro.md: -------------------------------------------------------------------------------- 1 | # Lifetime 2 | Learning resources: 3 | - English: [Rust Book 10.3](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html) 4 | - 简体中文: [Rust语言圣经 - 生命周期](https://course.rs/advance/lifetime/intro.html) 5 | 6 | -------------------------------------------------------------------------------- /en/src/macro.md: -------------------------------------------------------------------------------- 1 | # macro 2 | -------------------------------------------------------------------------------- /en/src/ownership/intro.md: -------------------------------------------------------------------------------- 1 | # Ownership and Borrowing 2 | Learning resources: 3 | - English: [Rust Book 4.1-4.4](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html) 4 | - 简体中文: [Rust语言圣经 - 所有权与借用](https://course.rs/basic/ownership/index.html) 5 | 6 | -------------------------------------------------------------------------------- /en/src/pattern-match/intro.md: -------------------------------------------------------------------------------- 1 | # Pattern Match 2 | Learning resources: 3 | - English: [Rust Book 18](https://doc.rust-lang.org/book/ch18-00-patterns.html) 4 | - 简体中文: [Rust语言圣经 - 模式匹配](https://course.rs/basic/match-pattern/intro.html) 5 | 6 | 7 | -------------------------------------------------------------------------------- /en/src/pattern-match/patterns.md: -------------------------------------------------------------------------------- 1 | # Patterns 2 | 3 | 1. 🌟🌟 Use `|` to match several values, use `..=` to match an inclusive range. 4 | ```rust,editable 5 | 6 | fn main() {} 7 | fn match_number(n: i32) { 8 | match n { 9 | // Match a single value 10 | 1 => println!("One!"), 11 | // Fill in the blank with `|`, DON'T use `..` or `..=` 12 | __ => println!("match 2 -> 5"), 13 | // Match an inclusive range 14 | 6..=10 => { 15 | println!("match 6 -> 10") 16 | }, 17 | _ => { 18 | println!("match -infinite -> 0 or 11 -> +infinite") 19 | } 20 | } 21 | } 22 | ``` 23 | 24 | 2. 🌟🌟🌟 The `@` operator lets us create a variable that holds a value, at the same time we are testing that value to see whether it matches a pattern. 25 | ```rust,editable 26 | 27 | struct Point { 28 | x: i32, 29 | y: i32, 30 | } 31 | 32 | fn main() { 33 | // Fill in the blank to let p match the second arm 34 | let p = Point { x: __, y: __ }; 35 | 36 | match p { 37 | Point { x, y: 0 } => println!("On the x axis at {}", x), 38 | // Second arm 39 | Point { x: 0..=5, y: y@ (10 | 20 | 30) } => println!("On the y axis at {}", y), 40 | Point { x, y } => println!("On neither axis: ({}, {})", x, y), 41 | } 42 | } 43 | ``` 44 | 45 | 3. 🌟🌟🌟 46 | 47 | ```rust,editable 48 | 49 | // Fix the errors 50 | enum Message { 51 | Hello { id: i32 }, 52 | } 53 | 54 | fn main() { 55 | let msg = Message::Hello { id: 5 }; 56 | 57 | match msg { 58 | Message::Hello { 59 | id: 3..=7, 60 | } => println!("Found an id in range [3, 7]: {}", id), 61 | Message::Hello { id: newid@10 | 11 | 12 } => { 62 | println!("Found an id in another range [10, 12]: {}", newid) 63 | } 64 | Message::Hello { id } => println!("Found some other id: {}", id), 65 | } 66 | } 67 | ``` 68 | 69 | 4. 🌟🌟 A match guard is an additional if condition specified after the pattern in a match arm that must also match, along with the pattern matching, for that arm to be chosen. 70 | ```rust,editable 71 | 72 | // Fill in the blank to make the code work, `split` MUST be used 73 | fn main() { 74 | let num = Some(4); 75 | let split = 5; 76 | match num { 77 | Some(x) __ => assert!(x < split), 78 | Some(x) => assert!(x >= split), 79 | None => (), 80 | } 81 | 82 | println!("Success!"); 83 | } 84 | ``` 85 | 86 | 5. 🌟🌟 Ignoring remaining parts of the value with `..` 87 | ```rust,editable 88 | 89 | // Fill the blank to make the code work 90 | fn main() { 91 | let numbers = (2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048); 92 | 93 | match numbers { 94 | __ => { 95 | assert_eq!(first, 2); 96 | assert_eq!(last, 2048); 97 | } 98 | } 99 | 100 | println!("Success!"); 101 | } 102 | ``` 103 | 104 | 6. 🌟🌟 Using pattern `&mut V` to match a mutable reference requires you to be very careful, due to `V` being a value after matching. 105 | 106 | ```rust,editable 107 | 108 | // FIX the error with least changing 109 | // DON'T remove any code line 110 | fn main() { 111 | let mut v = String::from("hello,"); 112 | let r = &mut v; 113 | 114 | match r { 115 | &mut value => value.push_str(" world!") 116 | } 117 | } 118 | ``` 119 | 120 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice/blob/master/solutions/pattern-match/patterns.md)(under the solutions path), but only use it when you need it 121 | -------------------------------------------------------------------------------- /en/src/result-panic/intro.md: -------------------------------------------------------------------------------- 1 | # Result and panic 2 | Learning resources: 3 | - English: [Rust Book 9.1, 9.2](https://doc.rust-lang.org/book/ch09-00-error-handling.html) 4 | - 简体中文: [Rust语言圣经 - 返回值和错误处理](https://course.rs/basic/result-error/intro.html) 5 | 6 | -------------------------------------------------------------------------------- /en/src/self-referential.md: -------------------------------------------------------------------------------- 1 | # Self referential 2 | -------------------------------------------------------------------------------- /en/src/smart-pointers/box.md: -------------------------------------------------------------------------------- 1 | # Box 2 | 3 | 1. 🌟 4 | ```rust,editable 5 | // Make it work 6 | fn main() { 7 | // Create a new box `b` that contains the integer 5 8 | assert_eq!(*b, 5); 9 | 10 | println!("Success!"); 11 | } 12 | ``` 13 | 14 | 2. 🌟 15 | ```rust,editable 16 | 17 | // Make it work 18 | fn main() { 19 | let b = Box::new("Hello"); 20 | print_boxed_string(b); 21 | } 22 | 23 | fn print_boxed_string(b : _) { 24 | println!("{}", b); 25 | } 26 | ``` 27 | 28 | 3. 🌟 29 | ```rust,editable 30 | 31 | // Make it work 32 | fn main() { 33 | let b1 = Box::new(5); 34 | let b2 = b1; 35 | assert_eq!(_, 5); 36 | 37 | println!("Success!"); 38 | } 39 | ``` 40 | 41 | 4. 🌟 42 | ```rust,editable 43 | 44 | // Make it work 45 | fn main() { 46 | // Create a box `b` with an array [1, 2, 3, 4, 5] 47 | // Print each integer in `b` 48 | } 49 | ``` 50 | 51 | 52 | > You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it 53 | -------------------------------------------------------------------------------- /en/src/smart-pointers/cell-refcell.md: -------------------------------------------------------------------------------- 1 | # Cell and RefCell 2 | -------------------------------------------------------------------------------- /en/src/smart-pointers/deref.md: -------------------------------------------------------------------------------- 1 | # Deref 2 | -------------------------------------------------------------------------------- /en/src/smart-pointers/drop.md: -------------------------------------------------------------------------------- 1 | # Drop 2 | -------------------------------------------------------------------------------- /en/src/smart-pointers/intro.md: -------------------------------------------------------------------------------- 1 | # Smart pointers 2 | 3 | -------------------------------------------------------------------------------- /en/src/smart-pointers/rc-arc.md: -------------------------------------------------------------------------------- 1 | # Rc and Arc 2 | -------------------------------------------------------------------------------- /en/src/std/String.md: -------------------------------------------------------------------------------- 1 | # String 2 | -------------------------------------------------------------------------------- /en/src/std/intro.md: -------------------------------------------------------------------------------- 1 | # Stand Library todo 2 | -------------------------------------------------------------------------------- /en/src/tests/assertions.md: -------------------------------------------------------------------------------- 1 | # Assertions 2 | -------------------------------------------------------------------------------- /en/src/tests/benchmark.md: -------------------------------------------------------------------------------- 1 | # Benchmark 2 | 3 | https://doc.rust-lang.org/unstable-book/library-features/test.html -------------------------------------------------------------------------------- /en/src/tests/intro.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | -------------------------------------------------------------------------------- /en/src/tests/unit-integration.md: -------------------------------------------------------------------------------- 1 | # Unit and Integration 2 | -------------------------------------------------------------------------------- /en/src/tests/write-tests.md: -------------------------------------------------------------------------------- 1 | # Write Tests 2 | -------------------------------------------------------------------------------- /en/src/threads/atomic.md: -------------------------------------------------------------------------------- 1 | # Atomic 2 | -------------------------------------------------------------------------------- /en/src/threads/basic-using.md: -------------------------------------------------------------------------------- 1 | # Basic using 2 | -------------------------------------------------------------------------------- /en/src/threads/intro.md: -------------------------------------------------------------------------------- 1 | # Threads 2 | -------------------------------------------------------------------------------- /en/src/threads/message-passing.md: -------------------------------------------------------------------------------- 1 | # Message passing 2 | -------------------------------------------------------------------------------- /en/src/threads/send-sync.md: -------------------------------------------------------------------------------- 1 | # Send and Sync 2 | -------------------------------------------------------------------------------- /en/src/threads/sync.md: -------------------------------------------------------------------------------- 1 | # Sync 2 | -------------------------------------------------------------------------------- /en/src/type-conversions/intro.md: -------------------------------------------------------------------------------- 1 | # Type Conversion 2 | Learning resources: 3 | - English: [Standary library](https://std.rs) 4 | - 简体中文: [Rust语言圣经 - 所有权与借用](https://course.rs/basic/converse.html) 5 | 6 | -------------------------------------------------------------------------------- /en/src/unsafe/intro.md: -------------------------------------------------------------------------------- 1 | # Unsafe todo 2 | -------------------------------------------------------------------------------- /en/src/weak.md: -------------------------------------------------------------------------------- 1 | # Weak and Circle reference 2 | -------------------------------------------------------------------------------- /en/src/why-exercise.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |

Practice Rust with challenging examples, exercises and projects

6 | 7 |
8 | 9 | [![Stars Count](https://img.shields.io/github/stars/sunface/rust-by-practice?style=flat)](https://github.com/sunface/rust-by-practice/stargazers) [![Forks Count](https://img.shields.io/github/forks/sunface/rust-by-practice.svg?style=flat)](https://github.com/naaive/orange/network/members) 10 | [![LICENSE](https://img.shields.io/badge/license-mit-green?style=flat)](https://github.com/sunface/rust-by-practice/blob/master/LICENSE) 11 |
12 | 13 | This book was designed for easily diving into and getting skilled with Rust, and it's very easy to use: All you need to do is to make each exercise compile without ERRORS and Panics ! 14 | 15 | 16 | ## Reading online 17 | 18 | - [English](https://practice.rs) 19 | - [简体中文](https://zh.practice.rs) 20 | 21 | 22 | ## Running locally 23 | 24 | We use [mdbook](https://rust-lang.github.io/mdBook/) building our exercises. You can run locally with below steps: 25 | ```shell 26 | $ git clone https://github.com/sunface/rust-by-practice 27 | $ cargo install mdbook 28 | $ cd rust-by-practice && mdbook serve en/ 29 | ``` 30 | 31 | ## Features 32 | 33 | Part of our examples and exercises are borrowed from [Rust By Example](https://github.com/rust-lang/rust-by-example), thanks for your great works! 34 | 35 | Although they are so awesome, we have our own secret weapons :) 36 | 37 | - There are three parts in each chapter: examples, exercises and practices 38 | 39 | - Besides examples, we have `a lot of exercises`, you can Read, Edit and Run them ONLINE 40 | 41 | - Covering nearly all aspects of Rust, such as async/await, threads, sync primitives, optimizing, standard libraries, tool chain, data structures and algorithms etc. 42 | 43 | - Every exercise has its own solutions 44 | 45 | - The overall difficulties are a bit higher and from easy to super hard: easy 🌟 medium 🌟🌟 hard 🌟🌟🌟 super hard 🌟🌟🌟🌟 46 | 47 | **What we want to do is to fill the gap between learning and getting started with real projects!** 48 | -------------------------------------------------------------------------------- /en/theme/style1.css: -------------------------------------------------------------------------------- 1 | @media only screen and (max-width:1080px) { 2 | .sidetoc { 3 | display: none !important; 4 | } 5 | } 6 | 7 | @media only screen and (min-width:1080px) { 8 | main { 9 | position: relative; 10 | padding-right: 170px; 11 | } 12 | .sidetoc { 13 | margin-left: auto; 14 | margin-right: auto; 15 | /*left: calc(100% + (var(--content-max-width))/4 - 180px);*/ 16 | left: calc(100% - 200px); 17 | position: absolute; 18 | } 19 | .pagetoc { 20 | position: fixed; 21 | width: 200px; 22 | height: calc(100vh - var(--menu-bar-height) - 10rem); 23 | overflow: auto; 24 | z-index: 1000; 25 | } 26 | .pagetoc a { 27 | border-left: 1px solid var(--sidebar-bg); 28 | color: var(--fg) !important; 29 | display: block; 30 | padding-bottom: 5px; 31 | padding-top: 5px; 32 | padding-left: 10px; 33 | text-align: left; 34 | text-decoration: none; 35 | font-size: 1.2rem; 36 | } 37 | .pagetoc a:hover, 38 | .pagetoc a.active { 39 | background: var(--sidebar-bg); 40 | color: var(--sidebar-fg) !important; 41 | } 42 | .pagetoc .active { 43 | background: var(--sidebar-bg); 44 | color: var(--sidebar-fg); 45 | } 46 | } 47 | 48 | .page-footer { 49 | margin-top: 50px; 50 | border-top: 1px solid #ccc; 51 | overflow: hidden; 52 | padding: 10px 0; 53 | color: gray; 54 | } 55 | 56 | /* 修改章节目录的间距 */ 57 | .chapter li.chapter-item { 58 | /* 没有文件时的文字颜色 */ 59 | color: #939da3; 60 | margin-top: 1rem; 61 | } 62 | 63 | /* 修改滚动条宽度 */ 64 | ::-webkit-scrollbar { 65 | width: 5px; 66 | } 67 | 68 | /* 表格靠左对齐 */ 69 | table { 70 | margin-left: 0 !important; 71 | } 72 | 73 | /* 只使用底部的页面跳转,因为左右两边的宽跳转会被 page-toc 遮盖 */ 74 | @media only screen and (max-width: 2560px) { 75 | .nav-wide-wrapper { display: none; } 76 | .nav-wrapper { display: block; } 77 | } 78 | @media only screen and (max-width: 2560px) { 79 | .sidebar-visible .nav-wide-wrapper { display: none; } 80 | .sidebar-visible .nav-wrapper { display: block; } 81 | } 82 | -------------------------------------------------------------------------------- /practices/doc-comments/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /practices/doc-comments/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "doc-comments" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /practices/doc-comments/Readme.md: -------------------------------------------------------------------------------- 1 | ## Doc comments 2 | A practice project used in [Comments and Docs](https://practice.rs/comments-docs.html) chapter. 3 | -------------------------------------------------------------------------------- /practices/doc-comments/src/compute.rs: -------------------------------------------------------------------------------- 1 | //! Do some complicated arithmetic that you can't do by yourself 2 | 3 | /// # Panics 4 | /// 5 | /// The function panics if the second argument is zero. 6 | /// 7 | /// ```rust,should_panic 8 | /// // panics on division by zero 9 | /// doc_comments::compute::div(10, 0); 10 | /// ``` 11 | pub fn div(a: i32, b: i32) -> i32 { 12 | if b == 0 { 13 | panic!("Divide-by-zero error"); 14 | } 15 | 16 | a / b 17 | } 18 | 19 | 20 | /// ``` 21 | /// # fn try_main() -> Result<(), String> { 22 | /// let res = doc_comments::compute::try_div(10, 1)?; 23 | /// # Ok(()) // returning from try_main 24 | /// # } 25 | /// # fn main() { 26 | /// # try_main().unwrap(); 27 | /// # 28 | /// # } 29 | /// ``` 30 | pub fn try_div(a: i32, b: i32) -> Result { 31 | if b == 0 { 32 | Err(String::from("Divide-by-zero")) 33 | } else { 34 | Ok(a / b) 35 | } 36 | } -------------------------------------------------------------------------------- /practices/doc-comments/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Doc comments 2 | //! 3 | //! A library for showing how to use doc comments 4 | 5 | pub mod compute; 6 | 7 | /// Add one to the given value and return a new value 8 | /// 9 | /// # Examples 10 | /// 11 | /// ``` 12 | /// let arg = 5; 13 | /// let answer = doc_comments::add_one(arg); 14 | /// 15 | /// assert_eq!(6, answer); 16 | /// ``` 17 | pub fn add_one(x: i32) -> i32 { 18 | x + 1 19 | } 20 | 21 | 22 | 23 | /** Add two to the given value and return a new value 24 | 25 | # Examples 26 | 27 | ``` 28 | let arg = 5; 29 | let answer = doc_comments::add_two(arg); 30 | 31 | assert_eq!(7, answer); 32 | ``` 33 | */ 34 | pub fn add_two(x: i32) -> i32 { 35 | x + 2 36 | } 37 | 38 | 39 | /// Add three to the given value and return a [`Option`] type 40 | pub fn add_three(x: i32) -> Option { 41 | Some(x + 3) 42 | } 43 | 44 | mod a { 45 | /// Add four to the given value and return a [`Option`] type 46 | /// [`crate::MySpecialFormatter`] 47 | pub fn add_four(x: i32) -> Option { 48 | Some(x + 4) 49 | } 50 | } 51 | 52 | struct MySpecialFormatter; 53 | 54 | 55 | -------------------------------------------------------------------------------- /practices/hello-package/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /practices/hello-package/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-package" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /practices/hello-package/Readme.md: -------------------------------------------------------------------------------- 1 | ## Hello Package 2 | A practice project used in [Crate and Module](https://practice.rs/crate-module/crate.html) chapter. 3 | 4 | This project will guide us to create a package with a binary crate and several library crates in it. 5 | 6 | The project structure is as below: 7 | ```shell 8 | . 9 | ├── Cargo.lock 10 | ├── Cargo.toml 11 | ├── Readme.md 12 | ├── src 13 | │   ├── back_of_house.rs 14 | │   ├── front_of_house 15 | │   │   ├── hosting.rs 16 | │   │   ├── mod.rs 17 | │   │   └── serving.rs 18 | │   ├── lib.rs 19 | │   └── main.rs 20 | ``` -------------------------------------------------------------------------------- /practices/hello-package/src/back_of_house.rs: -------------------------------------------------------------------------------- 1 | use crate::front_of_house; 2 | pub fn fix_incorrect_order() { 3 | cook_order(); 4 | front_of_house::serving::serve_order(); 5 | } 6 | 7 | pub fn cook_order() {} -------------------------------------------------------------------------------- /practices/hello-package/src/front_of_house/hosting.rs: -------------------------------------------------------------------------------- 1 | pub fn add_to_waitlist() {} 2 | 3 | pub fn seat_at_table() -> String { 4 | String::from("sit down please") 5 | } 6 | 7 | -------------------------------------------------------------------------------- /practices/hello-package/src/front_of_house/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod hosting; 2 | pub mod serving; -------------------------------------------------------------------------------- /practices/hello-package/src/front_of_house/serving.rs: -------------------------------------------------------------------------------- 1 | pub fn take_order() {} 2 | 3 | pub fn serve_order() {} 4 | 5 | pub fn take_payment() {} 6 | 7 | // Maybe you don't want the guest hearing the your complaining about them 8 | // So just make it private 9 | fn complain() {} -------------------------------------------------------------------------------- /practices/hello-package/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod front_of_house; 2 | mod back_of_house; 3 | 4 | pub use crate::front_of_house::hosting; 5 | 6 | pub fn eat_at_restaurant() -> String { 7 | front_of_house::hosting::add_to_waitlist(); 8 | 9 | back_of_house::cook_order(); 10 | 11 | String::from("yummy yummy!") 12 | } 13 | -------------------------------------------------------------------------------- /practices/hello-package/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | assert_eq!(hello_package::hosting::seat_at_table(), "sit down please"); 3 | assert_eq!(hello_package::eat_at_restaurant(),"yummy yummy!"); 4 | } 5 | -------------------------------------------------------------------------------- /scripts/link_solution: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Function: 4 | # 1. Link solution page to each practice page where there is no solution link 5 | # 2. Correct link address (i.e. non-corresponding address) 6 | # 7 | # Usage: 8 | # locate in the project root directory and execute the file 9 | # 10 | # Tips: 11 | # Use 'git diff' to show the change after executing this command 12 | 13 | set -e # exit on error 14 | 15 | SOLUTION_DIR="solutions" 16 | ZH_DIR="zh-CN/src" 17 | EN_DIR="en/src" 18 | LINK_PREFIX="https://github.com/sunface/rust-by-practice/blob/master" 19 | 20 | # create an array of the relative address of all markdown file about practice in both Chinese and English 21 | read -ra markdown_files <<< "$(echo "${ZH_DIR}"/**/*.md "${EN_DIR}"/**/*.md)" 22 | 23 | for mdfile in "${markdown_files[@]}"; do 24 | # if the corresponding solution file exist, check whether the practice file contians a 25 | # link to solution or not. If not, create a link 26 | solution_file=$( sed "s#\(${ZH_DIR}\|${EN_DIR}\)#${SOLUTION_DIR}#g" <<< "${mdfile}" ) 27 | if [[ -f "${solution_file}" ]]; then # exist solution file 28 | echo -n "${mdfile}" 29 | correct_link="${LINK_PREFIX}/${solution_file}" 30 | found=$(grep -Ei '^\s*>.*https://.*solution.*$' < "${mdfile}" || true) 31 | if [[ -z "${found}" ]]; then # not found link 32 | echo >> "${mdfile}" # some file ends without newline character 33 | if [[ "${mdfile}" == "${ZH_DIR}"/* ]]; then # written in Chinese 34 | echo "> 你可以在[这里](${correct_link})找到答案(在 solutions 路径下)" >> "${mdfile}" 35 | elif [[ "${mdfile}" == "${EN_DIR}"/* ]]; then # written in English 36 | echo "> You can find the solutions [here](${correct_link})(under the solutions path), but only use it when you need it :)" >> "${mdfile}" 37 | fi 38 | else # found link in file 39 | # simply correct all matched links 40 | sed -i 's#^\(.*\)>\(.*\)(https://[^)]*)\(.*\)solution\(.*\)$#\1>\2('"${correct_link}"')\3solution\4#' "${mdfile}" 41 | fi 42 | echo " --done" 43 | fi 44 | done 45 | -------------------------------------------------------------------------------- /solutions/basic-types/char-bool.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | use std::mem::size_of_val; 5 | 6 | fn main() { 7 | let c1 = 'a'; 8 | assert_eq!(size_of_val(&c1), 4); 9 | 10 | let c2 = '中'; 11 | assert_eq!(size_of_val(&c2), 4); 12 | } 13 | ``` 14 | 15 | 2. 16 | 17 | ```rust 18 | fn main() { 19 | let c1 = '中'; 20 | print_char(c1); 21 | } 22 | 23 | fn print_char(c: char) { 24 | println!("{}", c); 25 | } 26 | ``` 27 | 28 | 3. 29 | 30 | ```rust 31 | fn main() { 32 | let _f: bool = false; 33 | 34 | let t = false; 35 | if !t { 36 | println!("hello, world"); 37 | } 38 | } 39 | ``` 40 | 41 | 4. 42 | 43 | ```rust 44 | fn main() { 45 | let f = true; 46 | let t = true || false; 47 | assert_eq!(t, f); 48 | } 49 | ``` 50 | 51 | 5. 52 | 53 | ```rust 54 | fn main() { 55 | let v0: () = (); 56 | 57 | let v = (2, 3); 58 | assert_eq!(v0, implicitly_ret_unit()) 59 | } 60 | 61 | fn implicitly_ret_unit() { 62 | println!("I will return a ()") 63 | } 64 | 65 | // don't use this one 66 | fn explicitly_ret_unit() -> () { 67 | println!("I will return a ()") 68 | } 69 | ``` 70 | 71 | 6. 72 | 73 | ```rust 74 | use std::mem::size_of_val; 75 | 76 | fn main() { 77 | let unit: () = (); 78 | // unit type doesn't occupy any memory space 79 | assert!(size_of_val(&unit) == 0); 80 | } 81 | ``` 82 | 83 | 84 | -------------------------------------------------------------------------------- /solutions/basic-types/functions.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | // don't modify the following two lines! 6 | let (x, y) = (1, 2); 7 | let s = sum(x, y); 8 | 9 | assert_eq!(s, 3); 10 | } 11 | 12 | fn sum(x: i32, y: i32) -> i32 { 13 | x + y 14 | } 15 | ``` 16 | 17 | 2. 18 | 19 | ```rust 20 | fn main() { 21 | print(); 22 | } 23 | 24 | // replace i32 with another type 25 | fn print() -> () { 26 | println!("hello,world"); 27 | } 28 | ``` 29 | 30 | 3. 31 | 32 | ```rust 33 | fn main() { 34 | never_return(); 35 | } 36 | 37 | fn never_return() -> ! { 38 | // implement this function, don't modify fn signatures 39 | panic!("I return nothing!") 40 | } 41 | ``` 42 | 43 | ```rust 44 | fn main() { 45 | never_return(); 46 | } 47 | 48 | use std::thread; 49 | use std::time; 50 | 51 | fn never_return() -> ! { 52 | // implement this function, don't modify fn signatures 53 | loop { 54 | println!("I return nothing"); 55 | // sleeping for 1 second to avoid exhausting the cpu resource 56 | thread::sleep(time::Duration::from_secs(1)) 57 | } 58 | } 59 | ``` 60 | 61 | 4. 62 | 63 | ```rust 64 | fn main() { 65 | println!("Success!"); 66 | } 67 | 68 | fn get_option(tp: u8) -> Option { 69 | match tp { 70 | 1 => { 71 | // TODO 72 | } 73 | _ => { 74 | // TODO 75 | } 76 | }; 77 | 78 | never_return_fn() 79 | } 80 | 81 | // IMPLEMENT this function 82 | // DON'T change any code else 83 | fn never_return_fn() -> ! { 84 | unimplemented!() 85 | } 86 | ``` 87 | 88 | ```rust 89 | // IMPLEMENT this function in THREE ways 90 | fn never_return_fn() -> ! { 91 | panic!() 92 | } 93 | ``` 94 | 95 | ```rust 96 | // IMPLEMENT this function in THREE ways 97 | fn never_return_fn() -> ! { 98 | todo!(); 99 | } 100 | ``` 101 | 102 | ```rust 103 | // IMPLEMENT this function in THREE ways 104 | fn never_return_fn() -> ! { 105 | loop { 106 | std::thread::sleep(std::time::Duration::from_secs(1)) 107 | } 108 | } 109 | ``` 110 | 111 | 5. 112 | 113 | ```rust 114 | fn main() { 115 | // FILL in the blank 116 | let b = false; 117 | 118 | let _v = match b { 119 | true => 1, 120 | // Diverging functions can also be used in match expression 121 | false => { 122 | println!("Success!"); 123 | panic!("we have no value for `false`, but we can panic") 124 | } 125 | }; 126 | 127 | println!("Exercise Failed if printing out this line!"); 128 | } 129 | 130 | ``` -------------------------------------------------------------------------------- /solutions/basic-types/numbers.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | let x: i32 = 5; 6 | let mut y = 5; 7 | 8 | y = x; 9 | 10 | let z = 10; // type of z : i32 11 | } 12 | ``` 13 | 14 | 2. 15 | 16 | ```rust 17 | fn main() { 18 | let v: u16 = 38_u8 as u16; 19 | } 20 | ``` 21 | 22 | 3. 23 | 24 | ```rust 25 | fn main() { 26 | let x = 5; 27 | assert_eq!("i32".to_string(), type_of(&x)); 28 | } 29 | 30 | // get the type of given variable, return a string representation of the type , e.g "i8", "u8", "i32", "u32" 31 | fn type_of(_: &T) -> String { 32 | format!("{}", std::any::type_name::()) 33 | } 34 | ``` 35 | 36 | 4. 37 | 38 | ```rust 39 | fn main() { 40 | assert_eq!(i8::MAX, 127); 41 | assert_eq!(u8::MAX, 255); 42 | } 43 | ``` 44 | 45 | 5. 46 | 47 | ```rust 48 | fn main() { 49 | let v1 = 247_u8 + 8; 50 | let v2 = i8::checked_add(119, 8).unwrap(); 51 | println!("{},{}",v1,v2); 52 | } 53 | ``` 54 | 55 | 6. 56 | 57 | ```rust 58 | fn main() { 59 | let v = 1_024 + 0xff + 0o77 + 0b1111_1111; 60 | assert!(v == 1597); 61 | } 62 | ``` 63 | 64 | 7. 65 | 66 | ```rust 67 | fn main() { 68 | let x = 1_000.000_1; // f64 69 | let y: f32 = 0.12; // f32 70 | let z = 0.01_f64; // f64 71 | 72 | assert_eq!(type_of(&x), "f64".to_string()); 73 | println!("Success!"); 74 | } 75 | 76 | fn type_of(_: &T) -> String { 77 | format!("{}", std::any::type_name::()) 78 | } 79 | ``` 80 | 81 | 8. 82 | 83 | ```rust 84 | fn main() { 85 | assert!(0.1_f32+0.2_f32==0.3_f32); 86 | } 87 | ``` 88 | 89 | ```rust 90 | fn main() { 91 | assert!((0.1_f64+ 0.2 - 0.3).abs() < 0.001); 92 | } 93 | ``` 94 | 95 | 9. 96 | 97 | ```rust 98 | fn main() { 99 | let mut sum = 0; 100 | for i in -3..2 { 101 | sum += i 102 | } 103 | 104 | assert!(sum == -5); 105 | 106 | for c in 'a'..='z' { 107 | println!("{}",c as u8); 108 | } 109 | } 110 | ``` 111 | 112 | 10. 113 | 114 | ```rust 115 | use std::ops::{Range, RangeInclusive}; 116 | fn main() { 117 | assert_eq!((1..5), Range{ start: 1, end: 5 }); 118 | assert_eq!((1..=5), RangeInclusive::new(1, 5)); 119 | } 120 | ``` 121 | 122 | 11. 123 | 124 | ```rust 125 | fn main() { 126 | // Integer addition 127 | assert!(1u32 + 2 == 3); 128 | 129 | // Integer subtraction 130 | assert!(1i32 - 2 == -1); 131 | assert!(1i8 - 2 == -1); 132 | 133 | assert!(3 * 50 == 150); 134 | 135 | assert!(9 / 3 == 3); // error ! make it work 136 | 137 | assert!(24 % 5 == 4); 138 | // Short-circuiting boolean logic 139 | assert!(true && false == false); 140 | assert!(true || false == true); 141 | assert!(!true == false); 142 | 143 | // Bitwise operations 144 | println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101); 145 | println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101); 146 | println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101); 147 | println!("1 << 5 is {}", 1u32 << 5); 148 | println!("0x80 >> 2 is 0x{:x}", 0x80u32 >> 2); 149 | } 150 | ``` 151 | -------------------------------------------------------------------------------- /solutions/basic-types/statements.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | let v = { 6 | let mut x = 1; 7 | x += 2 8 | }; 9 | 10 | assert_eq!(v, ()); 11 | } 12 | ``` 13 | 14 | ```rust 15 | fn main() { 16 | let v = { 17 | let mut x = 1; 18 | x += 2; 19 | x 20 | }; 21 | 22 | assert_eq!(v, 3); 23 | } 24 | ``` 25 | 26 | 2. 27 | 28 | ```rust 29 | fn main() { 30 | let v = { 31 | let x = 3; 32 | x 33 | }; 34 | 35 | assert!(v == 3); 36 | } 37 | ``` 38 | 39 | 3. 40 | 41 | ```rust 42 | fn main() { 43 | let s = sum(1 , 2); 44 | assert_eq!(s, 3); 45 | } 46 | 47 | fn sum(x: i32, y: i32) -> i32 { 48 | x + y 49 | } 50 | ``` -------------------------------------------------------------------------------- /solutions/compound-types/array.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | let arr: [i32; 5] = [1, 2, 3, 4, 5]; 6 | 7 | assert!(arr.len() == 5); 8 | } 9 | ``` 10 | 11 | 2. 12 | 13 | ```rust 14 | fn main() { 15 | // we can ignore parts of the array type or even the whole type, let the compiler infer it for us 16 | let arr0 = [1, 2, 3]; 17 | let arr: [_; 3] = ['a', 'b', 'c']; 18 | 19 | // Arrays are stack allocated, `std::mem::size_of_val` return the bytes which array occupies 20 | // A char takes 4 byte in Rust: Unicode char 21 | assert!(std::mem::size_of_val(&arr) == 12); 22 | } 23 | ``` 24 | 25 | 3. 26 | 27 | ```rust 28 | fn main() { 29 | let list: [i32; 100] = [1; 100]; 30 | 31 | assert!(list[0] == 1); 32 | assert!(list.len() == 100); 33 | } 34 | ``` 35 | 36 | 4. 37 | 38 | ```rust 39 | fn main() { 40 | // fix the error 41 | let _arr = [1, 2, 3]; 42 | } 43 | ``` 44 | 45 | 5. 46 | 47 | ```rust 48 | fn main() { 49 | let arr = ['a', 'b', 'c']; 50 | 51 | let ele = arr[0]; 52 | 53 | assert!(ele == 'a'); 54 | } 55 | ``` 56 | 57 | 6. 58 | 59 | ```rust 60 | fn main() { 61 | let names = [String::from("Sunfei"), "Sunface".to_string()]; 62 | 63 | // `get` returns an Option, it's safe to use 64 | let name0 = names.get(0).unwrap(); 65 | 66 | // but indexing is not safe 67 | let _name1 = &names[1]; 68 | } 69 | ``` -------------------------------------------------------------------------------- /solutions/compound-types/slice.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | let arr = [1, 2, 3]; 6 | let s1: &[i32] = &arr[0..2]; 7 | 8 | let s2: &str = "hello, world"; 9 | } 10 | ``` 11 | 12 | 2. 13 | 14 | ```rust 15 | fn main() { 16 | let arr: [char; 3] = ['中', '国', '人']; 17 | 18 | let slice = &arr[..2]; 19 | 20 | // TIPS: slice( reference ) IS NOT an array, because if it is, then `assert!` will passed: each of the two UTF-8 chars '中' and '国' occupies 4 bytes, 2 * 4 = 8 21 | assert!(std::mem::size_of_val(&slice) == 16); 22 | } 23 | ``` 24 | 25 | 3. 26 | 27 | ```rust 28 | fn main() { 29 | let arr: [i32; 5] = [1, 2, 3, 4, 5]; 30 | let slice: &[i32] = &arr[1..4]; 31 | assert_eq!(slice, &[2, 3, 4]); 32 | } 33 | ``` 34 | 35 | 4. 36 | 37 | ```rust 38 | fn main() { 39 | let s = String::from("hello"); 40 | 41 | let slice1 = &s[0..2]; 42 | let slice2 = &s[..2]; 43 | 44 | assert_eq!(slice1, slice2); 45 | } 46 | ``` 47 | 48 | 5. 49 | 50 | ```rust 51 | fn main() { 52 | let s = "你好,世界"; 53 | let slice = &s[0..3]; 54 | 55 | assert!(slice == "你"); 56 | } 57 | ``` 58 | 59 | 6. 60 | 61 | ```rust 62 | fn main() { 63 | let mut s = String::from("hello world"); 64 | 65 | // here, &s is `&String` type, but `first_letter` needs a `&str` type. 66 | // it works because `&String` can be implicitly converted to `&str, If you want know more ,this is called `Deref` 67 | let letter = first_letter(&s); 68 | 69 | println!("the first letter is: {}", letter); 70 | 71 | s.clear(); 72 | } 73 | 74 | fn first_letter(s: &str) -> &str { 75 | &s[..1] 76 | } 77 | ``` 78 | 79 | -------------------------------------------------------------------------------- /solutions/compound-types/struct.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | struct Person { 5 | name: String, 6 | age: u8, 7 | hobby: String 8 | } 9 | fn main() { 10 | let age = 30; 11 | let p = Person { 12 | name: String::from("sunface"), 13 | age, 14 | hobby: "coding".to_string() 15 | }; 16 | } 17 | ``` 18 | 19 | 2. 20 | 21 | ```rust 22 | struct Unit; 23 | trait SomeTrait { 24 | // ...Some behavours defines here 25 | } 26 | 27 | // We don't care the the fields are in Unit, but we care its behaviors. 28 | // So we use a struct with no fields and implement some behaviors for it 29 | impl SomeTrait for Unit { } 30 | fn main() { 31 | let u = Unit; 32 | do_something_with_unit(u); 33 | } 34 | 35 | // fill the blank to make the code work 36 | fn do_something_with_unit(u: Unit) { } 37 | ``` 38 | 39 | 3. 40 | 41 | ```rust 42 | struct Color(i32, i32, i32); 43 | struct Point(i32, i32, i32); 44 | fn main() { 45 | let v = Point(0, 127, 255); 46 | check_color(v); 47 | } 48 | 49 | fn check_color(p: Point) { 50 | let Point(x, _, _) = p; 51 | assert_eq!(x, 0); 52 | assert_eq!(p.1, 127); 53 | assert_eq!(p.2, 255); 54 | } 55 | ``` 56 | 57 | 4. 58 | 59 | ```rust 60 | struct Person { 61 | name: String, 62 | age: u8, 63 | } 64 | fn main() { 65 | let age = 18; 66 | let mut p = Person { 67 | name: String::from("sunface"), 68 | age, 69 | }; 70 | 71 | // how can you believe sunface is only 18? 72 | p.age = 30; 73 | 74 | p.name = String::from("sunfei"); 75 | } 76 | ``` 77 | 78 | 5. 79 | 80 | ```rust 81 | struct Person { 82 | name: String, 83 | age: u8, 84 | } 85 | fn main() {} 86 | 87 | fn build_person(name: String, age: u8) -> Person { 88 | Person { 89 | age, 90 | name 91 | } 92 | } 93 | ``` 94 | 95 | 6. 96 | 97 | ```rust 98 | struct User { 99 | active: bool, 100 | username: String, 101 | email: String, 102 | sign_in_count: u64, 103 | } 104 | fn main() { 105 | let u1 = User { 106 | email: String::from("someone@example.com"), 107 | username: String::from("sunface"), 108 | active: true, 109 | sign_in_count: 1, 110 | }; 111 | 112 | let u2 = set_email(u1); 113 | } 114 | 115 | fn set_email(u: User) -> User { 116 | User { 117 | email: String::from("contact@im.dev"), 118 | ..u 119 | } 120 | } 121 | ``` 122 | 123 | 7. 124 | 125 | ```rust 126 | #[derive(Debug)] 127 | struct Rectangle { 128 | width: u32, 129 | height: u32, 130 | } 131 | 132 | fn main() { 133 | let scale = 2; 134 | let rect1 = Rectangle { 135 | width: dbg!(30 * scale), // print debug info to stderr and assign the value of `30 * scale` to `width` 136 | height: 50, 137 | }; 138 | 139 | dbg!(&rect1); // print debug info to stderr 140 | 141 | println!("{:?}", rect1); // print debug info to stdout 142 | } 143 | ``` 144 | 145 | 8. 146 | 147 | ```rust 148 | #[derive(Debug)] 149 | struct File { 150 | name: String, 151 | data: String, 152 | } 153 | fn main() { 154 | let f = File { 155 | name: String::from("readme.md"), 156 | data: "Rust By Practice".to_string() 157 | }; 158 | 159 | let _name = f.name; 160 | 161 | println!("{}", f.data); 162 | } 163 | ``` -------------------------------------------------------------------------------- /solutions/compound-types/tuple.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | let _t0: (u8,i16) = (0, -1); 6 | // Tuples can be tuple's members 7 | let _t1: (u8, (i16, u32)) = (0, (-1, 1)); 8 | let t: (u8, u16, i64, &str, String) = (1u8, 2u16, 3i64, "hello", String::from(", world")); 9 | } 10 | ``` 11 | 12 | 2. 13 | 14 | ```rust 15 | fn main() { 16 | let t = ("i", "am", "sunface"); 17 | assert_eq!(t.2, "sunface"); 18 | } 19 | ``` 20 | 21 | 3. 22 | 23 | ```rust 24 | fn main() { 25 | let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); 26 | println!("too long tuple: {:?}", too_long_tuple); 27 | } 28 | ``` 29 | 30 | 4. 31 | 32 | ```rust 33 | fn main() { 34 | let tup = (1, 6.4, "hello"); 35 | 36 | let (x, z, y) = tup; 37 | 38 | assert_eq!(x, 1); 39 | assert_eq!(y, "hello"); 40 | assert_eq!(z, 6.4); 41 | } 42 | ``` 43 | 44 | 5. 45 | 46 | ```rust 47 | fn main() { 48 | let (x, y, z); 49 | 50 | // fill the blank 51 | (y, z, x) = (1, 2, 3); 52 | 53 | assert_eq!(x, 3); 54 | assert_eq!(y, 1); 55 | assert_eq!(z, 2); 56 | } 57 | ``` 58 | 59 | 6. 60 | 61 | ```rust 62 | fn main() { 63 | let (x, y) = sum_multiply((2, 3)); 64 | 65 | assert_eq!(x, 5); 66 | assert_eq!(y, 6); 67 | } 68 | 69 | fn sum_multiply(nums: (i32, i32)) -> (i32, i32) { 70 | (nums.0 + nums.1, nums.0 * nums.1) 71 | } 72 | ``` -------------------------------------------------------------------------------- /solutions/crate-module/crate.md: -------------------------------------------------------------------------------- 1 | 1. `cargo new hello-package` 2 | 3 | 2. `cargo new --lib hello-package1` 4 | 5 | 3. `hello-package` has a binary crate named `hello-package`, `src/main.rs` is the crate root. 6 | 7 | `hello-pacakge1` has a library crate named `hello-package1`, `src/lib.rs` is the crate root. 8 | 9 | 4. `hello-package1` 10 | 11 | 5. 12 | 13 | ```shell 14 | # FILL in the blanks 15 | . 16 | ├── Cargo.lock 17 | ├── Cargo.toml 18 | ├── src 19 | │   ├── main.rs 20 | │   └── lib.rs 21 | ``` 22 | 23 | 6. 24 | 25 | ```shell 26 | # Create a package which contains 27 | # 1. three binary crates: `hello-package`, `main1` and `main2` 28 | # 2. one library crate 29 | # describe the directory tree below 30 | . 31 | ├── Cargo.toml 32 | ├── Cargo.lock 33 | ├── src 34 | │ ├── main.rs 35 | │ ├── lib.rs 36 | │ └── bin 37 | │ └── main1.rs 38 | │ └── main2.rs 39 | ├── tests # directory for integrated tests files 40 | │ └── some_integration_tests.rs 41 | ├── benches # dir for benchmark files 42 | │ └── simple_bench.rs 43 | └── examples # dir for example files 44 | └── simple_example.rs 45 | ``` -------------------------------------------------------------------------------- /solutions/crate-module/module.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | // in lib.rs 5 | mod front_of_house { 6 | mod hosting { 7 | fn add_to_waitlist() {} 8 | 9 | fn seat_at_table() {} 10 | } 11 | 12 | mod serving { 13 | fn take_order() {} 14 | 15 | fn serve_order() {} 16 | 17 | fn take_payment() {} 18 | 19 | fn complain() {} 20 | } 21 | } 22 | ``` 23 | 24 | 2. 25 | 26 | ```rust 27 | // in lib.rs 28 | pub mod front_of_house { 29 | pub mod hosting { 30 | pub fn add_to_waitlist() {} 31 | 32 | pub fn seat_at_table() {} 33 | } 34 | 35 | pub mod serving { 36 | pub fn take_order() {} 37 | 38 | pub fn serve_order() {} 39 | 40 | pub fn take_payment() {} 41 | 42 | // Maybe you don't want the guest hearing the your complaining about them 43 | // So just make it private 44 | fn complain() {} 45 | } 46 | } 47 | 48 | pub fn eat_at_restaurant() { 49 | // 绝对路径 50 | crate::front_of_house::hosting::add_to_waitlist(); 51 | 52 | // 相对路径 53 | front_of_house::hosting::add_to_waitlist(); 54 | } 55 | ``` 56 | 57 | 3. 58 | 59 | ```rust 60 | mod back_of_house { 61 | fn fix_incorrect_order() { 62 | cook_order(); 63 | super::front_of_house::serving::serve_order(); 64 | } 65 | 66 | fn cook_order() {} 67 | } 68 | ``` 69 | 70 | ```rust 71 | mod back_of_house { 72 | fn fix_incorrect_order() { 73 | cook_order(); 74 | crate::front_of_house::serving::serve_order(); 75 | } 76 | 77 | fn cook_order() {} 78 | } 79 | ``` 80 | 81 | 4. 82 | 83 | ```rust 84 | // in src/lib.rs 85 | 86 | pub mod front_of_house; 87 | pub mod back_of_house; 88 | 89 | pub fn eat_at_restaurant() -> String { 90 | front_of_house::hosting::add_to_waitlist(); 91 | 92 | back_of_house::cook_order(); 93 | 94 | String::from("yummy yummy!") 95 | } 96 | ``` 97 | 98 | ```rust 99 | // in src/back_of_house.rs 100 | 101 | use crate::front_of_house; 102 | pub fn fix_incorrect_order() { 103 | cook_order(); 104 | front_of_house::serving::serve_order(); 105 | } 106 | 107 | pub fn cook_order() {} 108 | ``` 109 | 110 | ```rust 111 | // in src/front_of_house/mod.rs 112 | 113 | pub mod hosting; 114 | pub mod serving; 115 | ``` 116 | 117 | ```rust 118 | // in src/front_of_house/hosting.rs 119 | 120 | pub fn add_to_waitlist() {} 121 | 122 | pub fn seat_at_table() -> String { 123 | String::from("sit down please") 124 | } 125 | ``` 126 | 127 | ```rust,editable 128 | // in src/front_of_house/serving.rs 129 | 130 | pub fn take_order() {} 131 | 132 | pub fn serve_order() {} 133 | 134 | pub fn take_payment() {} 135 | 136 | // Maybe you don't want the guest hearing the your complaining about them 137 | // So just make it private 138 | fn complain() {} 139 | ``` 140 | 141 | 5. 142 | 143 | ```rust 144 | fn main() { 145 | assert_eq!(hello_package::front_of_house::hosting::seat_at_table(), "sit down please"); 146 | assert_eq!(hello_package::eat_at_restaurant(), "yummy yummy!"); 147 | } 148 | ``` -------------------------------------------------------------------------------- /solutions/crate-module/use-pub.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | use std::fmt::Result; 5 | use std::io::Result as IoResult; 6 | 7 | fn main() {} 8 | ``` 9 | 10 | 2. 11 | 12 | ```rust 13 | use std::collections::*; 14 | 15 | fn main() { 16 | let _c1:HashMap<&str, i32> = HashMap::new(); 17 | let mut c2 = BTreeMap::new(); 18 | c2.insert(1, "a"); 19 | let _c3: HashSet = HashSet::new(); 20 | } 21 | ``` 22 | 23 | ```rust 24 | use std::collections::{HashMap, BTreeMap, HashSet}; 25 | 26 | fn main() { 27 | let _c1:HashMap<&str, i32> = HashMap::new(); 28 | let mut c2 = BTreeMap::new(); 29 | c2.insert(1, "a"); 30 | let _c3: HashSet = HashSet::new(); 31 | } 32 | ``` 33 | 34 | 3. 35 | 36 | ```rust 37 | // in lib.rs 38 | 39 | // Add this line 40 | pub use crate::front_of_house::hosting; 41 | ``` -------------------------------------------------------------------------------- /solutions/fight-compiler/borrowing.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | struct test { 5 | list: Vec, 6 | a: i32 7 | } 8 | 9 | impl test { 10 | pub fn new() -> Self { 11 | test { list:vec![1,2,3,4,5,6,7], a:0 } 12 | } 13 | 14 | pub fn run(&mut self) { 15 | for i in 0..self.list.len() { 16 | self.do_something(self.list[i]) 17 | } 18 | 19 | } 20 | 21 | pub fn do_something(&mut self, n: i32) { 22 | self.a = n; 23 | } 24 | } 25 | 26 | fn main() {} 27 | ``` -------------------------------------------------------------------------------- /solutions/formatted-output/debug-display.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | #[derive(Debug)] 5 | struct Structure(i32); 6 | 7 | fn main() { 8 | // Types in std and Rust have implemented the fmt::Debug trait 9 | println!("{:?} months in a year.", 12); 10 | 11 | println!("Now {:?} will print!", Structure(3)); 12 | } 13 | ``` 14 | 15 | 2. 16 | 17 | ```rust 18 | #[derive(Debug)] 19 | struct Person { 20 | name: String, 21 | age: u8 22 | } 23 | 24 | fn main() { 25 | let person = Person { name: "Sunface".to_string(), age: 18 }; 26 | 27 | println!("{:#?}", person); 28 | } 29 | ``` 30 | 31 | 3. 32 | 33 | ```rust 34 | use std::fmt; 35 | 36 | struct Structure(i32); 37 | 38 | struct Deep(Structure); 39 | impl fmt::Debug for Deep { 40 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 41 | write!(f, "{:?}", self.0.0) 42 | } 43 | } 44 | 45 | fn main() { 46 | // The problem with `derive` is there is no control over how 47 | // the results look. What if I want this to just show a `7`? 48 | 49 | /* Make it print: Now 7 will print! */ 50 | println!("Now {:?} will print!", Deep(Structure(7))); 51 | } 52 | ``` 53 | 54 | 4 55 | 56 | ```rust 57 | use std::fmt; 58 | 59 | struct Point2D { 60 | x: f64, 61 | y: f64, 62 | } 63 | 64 | impl fmt::Display for Point2D { 65 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 66 | write!(f, "Display: {} + {}i", self.x, self.y) 67 | } 68 | } 69 | 70 | impl fmt::Debug for Point2D { 71 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 72 | write!(f, "Debug: Complex {{ real: {:?}, imag: {:?} }}", self.x, self.y) 73 | } 74 | } 75 | 76 | fn main() { 77 | let point = Point2D { x: 3.3, y: 7.2 }; 78 | 79 | assert_eq!(format!("{}",point), "Display: 3.3 + 7.2i"); 80 | assert_eq!(format!("{:?}",point), "Debug: Complex { real: 3.3, imag: 7.2 }"); 81 | 82 | println!("Success!") 83 | } 84 | ``` 85 | 86 | 5. 87 | 88 | ```rust 89 | use std::fmt; // Import the `fmt` module. 90 | 91 | // Define a structure named `List` containing a `Vec`. 92 | struct List(Vec); 93 | 94 | impl fmt::Display for List { 95 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 96 | // Extract the value using tuple indexing, 97 | // and create a reference to `vec`. 98 | let vec = &self.0; 99 | 100 | write!(f, "[")?; 101 | 102 | // Iterate over `v` in `vec` while enumerating the iteration 103 | // count in `count`. 104 | for (count, v) in vec.iter().enumerate() { 105 | // For every element except the first, add a comma. 106 | // Use the ? operator to return on errors. 107 | if count != 0 { write!(f, ", ")?; } 108 | write!(f, "{}: {}",count, v)?; 109 | } 110 | 111 | // Close the opened bracket and return a fmt::Result value. 112 | write!(f, "]") 113 | } 114 | } 115 | 116 | fn main() { 117 | let v = List(vec![1, 2, 3]); 118 | assert_eq!(format!("{}",v), "[0: 1, 1: 2, 2: 3]"); 119 | } 120 | ``` -------------------------------------------------------------------------------- /solutions/formatted-output/println.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | let s1 = "hello"; 6 | /* Fill in the blank */ 7 | let s = format!("{}, world!", s1); 8 | assert_eq!(s, "hello, world!"); 9 | } 10 | ``` 11 | 12 | 2. 13 | 14 | ```rust 15 | fn main() { 16 | print!("hello world, "); 17 | println!("I am"); 18 | println!("Sunface!"); 19 | } 20 | ``` -------------------------------------------------------------------------------- /solutions/generics-traits/const-generics.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | struct Array { 5 | data : [T; N] 6 | } 7 | 8 | fn main() { 9 | let arrays = [ 10 | Array{ 11 | data: [1, 2, 3], 12 | }, 13 | Array { 14 | data: [1, 2, 3], 15 | }, 16 | Array { 17 | data: [1, 2, 4] 18 | } 19 | ]; 20 | } 21 | ``` 22 | 23 | 2. 24 | 25 | ```rust 26 | fn print_array(arr: [T; N]) { 27 | println!("{:?}", arr); 28 | } 29 | fn main() { 30 | let arr = [1, 2, 3]; 31 | print_array(arr); 32 | 33 | let arr = ["hello", "world"]; 34 | print_array(arr); 35 | } 36 | ``` 37 | 38 | 3. 39 | 40 | ```rust 41 | #![allow(incomplete_features)] 42 | #![feature(generic_const_exprs)] 43 | 44 | fn check_size(val: T) 45 | where 46 | Assert<{ core::mem::size_of::() < 768 }>: IsTrue, 47 | { 48 | //... 49 | } 50 | 51 | // fix the errors in main 52 | fn main() { 53 | check_size([0u8; 767]); 54 | check_size([0i32; 191]); 55 | check_size(["hello你好"; 47]); // &str is a string reference, containing a pointer and string length in it, so it takes two word long, in x86-64, 1 word = 8 bytes 56 | check_size([(); 31].map(|_| "hello你好".to_string())); // String is a smart pointer struct, it has three fields: pointer, length and capacity, each takes 8 bytes 57 | check_size(['中'; 191]); // A char takes 4 bytes in Rust 58 | } 59 | 60 | 61 | 62 | pub enum Assert {} 63 | 64 | pub trait IsTrue {} 65 | 66 | impl IsTrue for Assert {} 67 | ``` -------------------------------------------------------------------------------- /solutions/generics-traits/generics.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | struct A; // Concrete type `A`. 5 | struct S(A); // Concrete type `S`. 6 | struct SGen(T); // Generic type `SGen`. 7 | 8 | fn reg_fn(_s: S) {} 9 | 10 | fn gen_spec_t(_s: SGen
) {} 11 | 12 | fn gen_spec_i32(_s: SGen) {} 13 | 14 | fn generic(_s: SGen) {} 15 | 16 | fn main() { 17 | // Using the non-generic functions 18 | reg_fn(S(A)); // Concrete type. 19 | gen_spec_t(SGen(A)); // Implicitly specified type parameter `A`. 20 | gen_spec_i32(SGen(6)); // Implicitly specified type parameter `i32`. 21 | 22 | // Explicitly specified type parameter `char` to `generic()`. 23 | generic::(SGen('a')); 24 | 25 | // Implicitly specified type parameter `char` to `generic()`. 26 | generic(SGen('c')); 27 | } 28 | ``` 29 | 30 | 2. 31 | 32 | ```rust 33 | fn sum>(x: T, y: T) -> T { 34 | x + y 35 | } 36 | 37 | fn main() { 38 | assert_eq!(5, sum(2i8, 3i8)); 39 | assert_eq!(50, sum(20, 30)); 40 | assert_eq!(2.46, sum(1.23, 1.23)); 41 | } 42 | ``` 43 | 44 | 3. 45 | 46 | ```rust 47 | struct Point { 48 | x: T, 49 | y: T, 50 | } 51 | 52 | fn main() { 53 | let integer = Point { x: 5, y: 10 }; 54 | let float = Point { x: 1.0, y: 4.0 }; 55 | } 56 | ``` 57 | 58 | 4. 59 | 60 | ```rust 61 | // modify this struct to make the code work 62 | struct Point { 63 | x: T, 64 | y: U, 65 | } 66 | 67 | fn main() { 68 | // DON'T modify here 69 | let p = Point{x: 5, y : "hello".to_string()}; 70 | } 71 | ``` 72 | 73 | 5. 74 | 75 | ```rust 76 | struct Val { 77 | val: T, 78 | } 79 | 80 | impl Val { 81 | fn value(&self) -> &T { 82 | &self.val 83 | } 84 | } 85 | 86 | 87 | fn main() { 88 | let x = Val{ val: 3.0 }; 89 | let y = Val{ val: "hello".to_string()}; 90 | println!("{}, {}", x.value(), y.value()); 91 | } 92 | ``` 93 | 94 | 6. 95 | 96 | ```rust 97 | struct Point { 98 | x: T, 99 | y: U, 100 | } 101 | 102 | impl Point { 103 | fn mixup(self, other: Point) -> Point { 104 | Point { 105 | x: self.x, 106 | y: other.y, 107 | } 108 | } 109 | } 110 | 111 | fn main() { 112 | let p1 = Point { x: 5, y: 10 }; 113 | let p2 = Point { x: "Hello", y: '中'}; 114 | 115 | let p3 = p1.mixup(p2); 116 | 117 | assert_eq!(p3.x, 5); 118 | assert_eq!(p3.y, '中'); 119 | } 120 | ``` 121 | 122 | 7. 123 | 124 | ```rust 125 | struct Point { 126 | x: T, 127 | y: T, 128 | } 129 | 130 | impl Point { 131 | fn distance_from_origin(&self) -> f32 { 132 | (self.x.powi(2) + self.y.powi(2)).sqrt() 133 | } 134 | } 135 | 136 | fn main() { 137 | let p = Point{x: 5.0_f32, y: 10.0_f32}; 138 | println!("{}",p.distance_from_origin()) 139 | } 140 | ``` 141 | -------------------------------------------------------------------------------- /solutions/lifetime/advance.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | struct DoubleRef<'a,'b:'a, T> { 5 | r: &'a T, 6 | s: &'b T 7 | } 8 | fn main() { 9 | println!("Success!") 10 | } 11 | ``` 12 | 13 | 2. 14 | 15 | ```rust 16 | struct ImportantExcerpt<'a> { 17 | part: &'a str, 18 | } 19 | 20 | impl<'a: 'b, 'b> ImportantExcerpt<'a> { 21 | fn announce_and_return_part(&'a self, announcement: &'b str) -> &'b str { 22 | println!("Attention please: {}", announcement); 23 | self.part 24 | } 25 | } 26 | 27 | fn main() { 28 | println!("Success!") 29 | } 30 | ``` 31 | 32 | 3. 33 | 34 | ```rust 35 | fn f<'a, 'b>(x: &'a i32, mut y: &'b i32) where 'a: 'b { 36 | y = x; // &'a i32 is a subtype of &'b i32 because 'a: 'b 37 | let r: &'b &'a i32 = &&0; // &'b &'a i32 is well formed because 'a: 'b 38 | } 39 | fn main() { 40 | println!("Success!") 41 | } 42 | ``` 43 | 44 | 4. 45 | 46 | ```rust 47 | fn call_on_ref_zero(f: F) where for<'a> F: Fn(&'a i32) { 48 | let zero = 0; 49 | f(&zero); 50 | } 51 | 52 | fn main() { 53 | println!("Success!") 54 | } 55 | ``` 56 | 57 | Higher-ranked lifetimes may also be specified just before the trait: the only difference is the scope of the lifetime 58 | parameter, which extends only to the end of the following trait instead of the whole bound. This function is equivalent 59 | to the last one. 60 | 61 | ```rust 62 | fn call_on_ref_zero(f: F) where F: for<'a> Fn(&'a i32) { 63 | let zero = 0; 64 | f(&zero); 65 | } 66 | ``` 67 | 68 | 5. 69 | 70 | ```rust 71 | fn main() { 72 | let mut data = 10; 73 | let ref1 = &mut data; 74 | let ref2 = &mut *ref1; 75 | 76 | *ref2 += 2; 77 | *ref1 += 1; 78 | 79 | println!("{}", data); 80 | } 81 | ``` 82 | 83 | 6. 84 | 85 | ```rust 86 | struct Interface<'b, 'a: 'b> { 87 | manager: &'b mut Manager<'a> 88 | } 89 | 90 | impl<'b, 'a: 'b> Interface<'b, 'a> { 91 | pub fn noop(self) { 92 | println!("interface consumed"); 93 | } 94 | } 95 | 96 | struct Manager<'a> { 97 | text: &'a str 98 | } 99 | 100 | struct List<'a> { 101 | manager: Manager<'a>, 102 | } 103 | 104 | impl<'a> List<'a> { 105 | pub fn get_interface<'b>(&'b mut self) -> Interface<'b, 'a> 106 | where 'a: 'b { 107 | Interface { 108 | manager: &mut self.manager 109 | } 110 | } 111 | } 112 | 113 | fn main() { 114 | 115 | let mut list = List { 116 | manager: Manager { 117 | text: "hello" 118 | } 119 | }; 120 | 121 | list.get_interface().noop(); 122 | 123 | println!("Interface should be dropped here and the borrow released"); 124 | 125 | use_list(&list); 126 | } 127 | 128 | fn use_list(list: &List) { 129 | println!("{}", list.manager.text); 130 | } 131 | ``` -------------------------------------------------------------------------------- /solutions/lifetime/static.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | let v: &str = "hello"; 6 | need_static(v); 7 | 8 | println!("Success!") 9 | } 10 | 11 | fn need_static(r : &'static str) { 12 | assert_eq!(r, "hello"); 13 | } 14 | ``` 15 | 16 | ```rust 17 | fn main() { 18 | const v: &str = "hello"; 19 | need_static(v); 20 | 21 | println!("Success!") 22 | } 23 | 24 | fn need_static(r : &'static str) { 25 | assert_eq!(r, "hello"); 26 | } 27 | ``` 28 | 29 | 2. 30 | 31 | ```rust 32 | #[derive(Debug)] 33 | struct Config { 34 | a: String, 35 | b: String, 36 | } 37 | static mut config: Option<&mut Config> = None; 38 | 39 | fn init() -> Option<&'static mut Config> { 40 | let c = Box::new(Config { 41 | a: "A".to_string(), 42 | b: "B".to_string(), 43 | }); 44 | 45 | Some(Box::leak(c)) 46 | } 47 | 48 | 49 | fn main() { 50 | unsafe { 51 | config = init(); 52 | 53 | println!("{:?}",config) 54 | } 55 | } 56 | ``` 57 | 58 | 3. 59 | 60 | ```rust 61 | fn main() { 62 | // Make a `string` literal and print it: 63 | let static_string = "I'm in read-only memory"; 64 | println!("static_string: {}", static_string); 65 | 66 | println!("static_string reference remains alive: {}", static_string); 67 | } 68 | ``` 69 | 70 | 5. 71 | 72 | ```rust 73 | use std::fmt::Debug; 74 | 75 | fn print_it( input: T) { 76 | println!( "'static value passed in is: {:?}", input ); 77 | } 78 | 79 | fn print_it1( input: impl Debug + 'static ) { 80 | println!( "'static value passed in is: {:?}", input ); 81 | } 82 | 83 | 84 | fn print_it2( input: &T) { 85 | println!( "'static value passed in is: {:?}", input ); 86 | } 87 | 88 | fn main() { 89 | // i is owned and contains no references, thus it's 'static: 90 | const i:i32 = 5; 91 | print_it(i); 92 | 93 | // oops, &i only has the lifetime defined by the scope of 94 | // main(), so it's not 'static: 95 | print_it(&i); 96 | 97 | print_it1(&i); 98 | 99 | // but this one WORKS ! 100 | print_it2(&i); 101 | } 102 | ``` 103 | -------------------------------------------------------------------------------- /solutions/method.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | struct Rectangle { 5 | width: u32, 6 | height: u32, 7 | } 8 | 9 | impl Rectangle { 10 | fn area(&self) -> u32 { 11 | self.width * self.height 12 | } 13 | } 14 | 15 | fn main() { 16 | let rect1 = Rectangle { width: 30, height: 50 }; 17 | 18 | assert_eq!(rect1.area(), 1500); 19 | } 20 | ``` 21 | 22 | 2. 23 | 24 | ```rust 25 | #[derive(Debug)] 26 | struct TrafficLight { 27 | color: String, 28 | } 29 | 30 | impl TrafficLight { 31 | pub fn show_state(&self) { 32 | println!("the current state is {}", self.color); 33 | } 34 | } 35 | 36 | fn main() { 37 | let light = TrafficLight { 38 | color: "red".to_owned(), 39 | }; 40 | // Don't take the ownership of `light` here 41 | light.show_state(); 42 | // ..otherwise, there will be an error below 43 | println!("{:?}", light); 44 | } 45 | ``` 46 | 47 | 3. 48 | 49 | ```rust 50 | struct TrafficLight { 51 | color: String, 52 | } 53 | 54 | impl TrafficLight { 55 | // using `Self` to fill in the blank 56 | pub fn show_state(self: &Self) { 57 | println!("the current state is {}", self.color); 58 | } 59 | 60 | // fill in the blank, DON'T use any variants of `Self` 61 | pub fn change_state(&mut self) { 62 | self.color = "green".to_string() 63 | } 64 | } 65 | 66 | fn main() {} 67 | ``` 68 | 69 | 4. 70 | 71 | ```rust 72 | #[derive(Debug)] 73 | struct TrafficLight { 74 | color: String, 75 | } 76 | 77 | impl TrafficLight { 78 | // 1. implement a associated function `new`, 79 | // 2. it will return a TrafficLight contains color "red" 80 | // 3. must use `Self`, DONT use `TrafficLight` 81 | pub fn new() -> Self { 82 | Self { 83 | color: "red".to_string() 84 | } 85 | } 86 | 87 | pub fn get_state(&self) -> &str { 88 | &self.color 89 | } 90 | } 91 | 92 | fn main() { 93 | let light = TrafficLight::new(); 94 | assert_eq!(light.get_state(), "red"); 95 | } 96 | ``` 97 | 98 | 5. 99 | 100 | ```rust 101 | struct Rectangle { 102 | width: u32, 103 | height: u32, 104 | } 105 | 106 | // rewrite Rectangle to use multiple `impl` blocks 107 | impl Rectangle { 108 | fn area(&self) -> u32 { 109 | self.width * self.height 110 | } 111 | } 112 | 113 | impl Rectangle { 114 | fn can_hold(&self, other: &Rectangle) -> bool { 115 | self.width > other.width && self.height > other.height 116 | } 117 | } 118 | 119 | fn main() {} 120 | ``` 121 | 122 | 6. 123 | 124 | ```rust 125 | #[derive(Debug)] 126 | enum TrafficLightColor { 127 | Red, 128 | Yellow, 129 | Green, 130 | } 131 | 132 | // implement TrafficLightColor with a method 133 | impl TrafficLightColor { 134 | fn color(&self) -> String { 135 | match *self { 136 | TrafficLightColor::Red => "red".to_string(), 137 | TrafficLightColor::Yellow => "yellow".to_string(), 138 | TrafficLightColor::Green => "green".to_string(), 139 | } 140 | } 141 | } 142 | 143 | fn main() { 144 | let c = TrafficLightColor::Yellow; 145 | 146 | assert_eq!(c.color(), "yellow"); 147 | 148 | println!("{:?}", c); 149 | } 150 | ``` -------------------------------------------------------------------------------- /solutions/newtype-sized.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | use std::fmt; 5 | 6 | struct Wrapper(Vec); 7 | 8 | impl fmt::Display for Wrapper { 9 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 10 | write!(f, "[{}]", self.0.join(", ")) 11 | } 12 | } 13 | 14 | fn main() { 15 | let w = Wrapper(vec![String::from("hello"), String::from("world")]); 16 | println!("w = {}", w); 17 | } 18 | ``` 19 | 20 | 2. 21 | 22 | ```rust 23 | struct Meters(u32); 24 | 25 | fn main() { 26 | let i: u32 = 2; 27 | assert_eq!(i.pow(2), 4); 28 | 29 | let n = Meters(i); 30 | assert_eq!(n.0.pow(2), 4); 31 | } 32 | ``` 33 | 34 | 3. 35 | 36 | ```rust 37 | struct Years(i64); 38 | 39 | struct Days(i64); 40 | 41 | impl Years { 42 | pub fn to_days(&self) -> Days { 43 | Days(self.0 * 365) 44 | } 45 | } 46 | 47 | 48 | impl Days { 49 | pub fn to_years(&self) -> Years { 50 | Years(self.0 / 365) 51 | } 52 | } 53 | 54 | // an age verification function that checks age in years, must be given a value of type Years. 55 | fn old_enough(age: &Years) -> bool { 56 | age.0 >= 18 57 | } 58 | 59 | fn main() { 60 | let age = Years(5); 61 | let age_days = age.to_days(); 62 | println!("Old enough {}", old_enough(&age)); 63 | println!("Old enough {}", old_enough(&age_days.to_years())); 64 | } 65 | ``` 66 | 67 | 4. Sometimes `newtype` pattern can provide extra readability. 68 | 69 | ```rust 70 | use std::ops::Add; 71 | use std::fmt::{self, format}; 72 | 73 | struct Meters(u32); 74 | 75 | impl fmt::Display for Meters { 76 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 77 | write!(f, "There are still {} meters left", self.0) 78 | } 79 | } 80 | 81 | impl Add for Meters { 82 | type Output = Self; 83 | 84 | fn add(self, other: Meters) -> Self { 85 | Self(self.0 + other.0) 86 | } 87 | } 88 | 89 | fn main() { 90 | let d = calculate_distance(Meters(10), Meters(20)); 91 | assert_eq!(format!("{}", d), "There are still 30 meters left"); 92 | } 93 | 94 | /* implement calculate_distance */ 95 | fn calculate_distance(d1: Meters, d2: Meters) -> Meters { 96 | d1 + d2 97 | } 98 | ``` 99 | 100 | 5. 101 | 102 | ```rust 103 | enum VeryVerboseEnumOfThingsToDoWithNumbers { 104 | Add, 105 | Subtract, 106 | } 107 | 108 | /* Fill in the blank */ 109 | type Operations = VeryVerboseEnumOfThingsToDoWithNumbers; 110 | 111 | fn main() { 112 | // We can refer to each variant via its alias, not its long and inconvenient 113 | // name. 114 | let x = Operations::Add; 115 | } 116 | ``` 117 | 118 | 6. 119 | 120 | ```rust 121 | enum VeryVerboseEnumOfThingsToDoWithNumbers { 122 | Add, 123 | Subtract, 124 | } 125 | 126 | impl VeryVerboseEnumOfThingsToDoWithNumbers { 127 | fn run(&self, x: i32, y: i32) -> i32 { 128 | match self { 129 | Self::Add => x + y, 130 | Self::Subtract => x - y, 131 | } 132 | } 133 | } 134 | 135 | fn main() {} 136 | ``` 137 | 138 | 7. 139 | 140 | ```rust 141 | fn my_function() -> [u32; N] { 142 | [123; N] 143 | } 144 | 145 | fn main() { 146 | let arr = my_function::<5>(); 147 | println!("{:?}", arr); 148 | } 149 | ``` 150 | 151 | 8. 152 | 153 | ```rust 154 | fn main() { 155 | let s: &str = "Hello there!"; 156 | 157 | let arr: &[u8] = &[1, 2, 3]; 158 | } 159 | ``` 160 | 161 | 9. 162 | 163 | ```rust 164 | use std::fmt::Display; 165 | 166 | fn foobar_1(thing: &dyn Display) {} 167 | 168 | fn foobar_2(thing: Box) {} 169 | 170 | fn main() {} 171 | ``` -------------------------------------------------------------------------------- /solutions/ownership/borrowing.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | let x = 5; 6 | // fill the blank 7 | let p = &x; 8 | 9 | println!("the memory address of x is {:p}", p); // one possible output: 0x16fa3ac84 10 | } 11 | ``` 12 | 13 | 2. 14 | 15 | ```rust 16 | fn main() { 17 | let x = 5; 18 | let y = &x; 19 | 20 | // modify this line only 21 | assert_eq!(5, *y); 22 | } 23 | ``` 24 | 25 | 3. 26 | 27 | ```rust 28 | fn main() { 29 | let mut s = String::from("hello, "); 30 | 31 | borrow_object(&s) 32 | } 33 | 34 | fn borrow_object(s: &String) {} 35 | ``` 36 | 37 | 4. 38 | 39 | ```rust 40 | fn main() { 41 | let mut s = String::from("hello, "); 42 | 43 | push_str(&mut s) 44 | } 45 | 46 | fn push_str(s: &mut String) { 47 | s.push_str("world") 48 | } 49 | ``` 50 | 51 | 5. 52 | 53 | ```rust 54 | fn main() { 55 | let mut s = String::from("hello, "); 56 | 57 | // fill the blank to make it work 58 | let p = &mut s; 59 | 60 | p.push_str("world"); 61 | } 62 | ``` 63 | 64 | 6. 65 | 66 | ```rust 67 | fn main() { 68 | let c = '中'; 69 | 70 | let r1 = &c; 71 | // fill the blank,dont change other code 72 | let ref r2 = c; 73 | 74 | assert_eq!(*r1, *r2); 75 | 76 | // check the equality of the two address strings 77 | assert_eq!(get_addr(r1),get_addr(r2)); 78 | } 79 | 80 | // get memory address string 81 | fn get_addr(r: &char) -> String { 82 | format!("{:p}", r) 83 | } 84 | ``` 85 | 86 | 7. 87 | 88 | ```rust 89 | fn main() { 90 | let s = String::from("hello"); 91 | 92 | let r1 = &s; 93 | let r2 = &s; 94 | 95 | println!("{}, {}", r1, r2); 96 | } 97 | ``` 98 | 99 | 8. 100 | 101 | ```rust 102 | fn main() { 103 | //fix error by modifying this line 104 | let mut s = String::from("hello, "); 105 | 106 | borrow_object(&mut s) 107 | } 108 | 109 | fn borrow_object(s: &mut String) {} 110 | ``` 111 | 112 | 9. 113 | 114 | ```rust 115 | fn main() { 116 | let mut s = String::from("hello, "); 117 | 118 | borrow_object(&s); 119 | 120 | s.push_str("world"); 121 | } 122 | 123 | fn borrow_object(s: &String) {} 124 | ``` 125 | 126 | 10. 127 | 128 | ```rust 129 | fn main() { 130 | let mut s = String::from("hello, "); 131 | 132 | let r1 = &mut s; 133 | r1.push_str("world"); 134 | let r2 = &mut s; 135 | r2.push_str("!"); 136 | 137 | // println!("{}",r1); 138 | } 139 | ``` 140 | 141 | 11. 142 | 143 | ```rust 144 | fn main() { 145 | let mut s = String::from("hello, "); 146 | 147 | let r1 = &mut s; 148 | let r2 = &mut s; 149 | 150 | // add one line below to make a compiler error: cannot borrow `s` as mutable more than once at a time 151 | // you can't use r1 and r2 at the same time 152 | println!("{}, {}", r1, r2); 153 | } 154 | ``` 155 | -------------------------------------------------------------------------------- /solutions/ownership/ownership.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | let x = String::from("hello, world"); 6 | let y = x.clone(); 7 | println!("{},{}",x,y); 8 | } 9 | ``` 10 | 11 | ```rust 12 | fn main() { 13 | let x = "hello, world"; 14 | let y = x; 15 | println!("{},{}",x,y); 16 | } 17 | ``` 18 | 19 | ```rust 20 | fn main() { 21 | let x = &String::from("hello, world"); 22 | let y = x; 23 | println!("{},{}",x,y); 24 | } 25 | ``` 26 | ```rust 27 | fn main() { 28 | let x = String::from("hello, world"); 29 | let y = x.as_str(); 30 | println!("{},{}",x,y); 31 | } 32 | ``` 33 | 34 | ``` 35 | fn main() { 36 | let x = String::from("hello, world"); 37 | let y = &x; 38 | println!("{},{}",x,y); 39 | } 40 | 41 | ``` 42 | 43 | 2. 44 | 45 | ```rust 46 | // Don't modify code in main! 47 | fn main() { 48 | let s1 = String::from("hello, world"); 49 | let s2 = take_ownership(s1); 50 | 51 | println!("{}", s2); 52 | } 53 | 54 | // Only modify the code below! 55 | fn take_ownership(s: String) -> String { 56 | println!("{}", s); 57 | s 58 | } 59 | ``` 60 | 61 | 3. 62 | 63 | ```rust 64 | fn main() { 65 | let s = give_ownership(); 66 | println!("{}", s); 67 | } 68 | 69 | // Only modify the code below! 70 | fn give_ownership() -> String { 71 | let s = String::from("hello, world"); 72 | // convert String to Vec 73 | let _s = s.as_bytes(); 74 | s 75 | } 76 | ``` 77 | 78 | ```rust 79 | fn main() { 80 | let s = give_ownership(); 81 | println!("{}", s); 82 | } 83 | 84 | // Only modify the code below! 85 | fn give_ownership() -> String { 86 | let s = String::from("hello, world"); 87 | s 88 | } 89 | ``` 90 | 91 | 4. 92 | 93 | ```rust 94 | fn main() { 95 | let s = String::from("hello, world"); 96 | 97 | print_str(s.clone()); 98 | 99 | println!("{}", s); 100 | } 101 | 102 | fn print_str(s: String) { 103 | println!("{}",s) 104 | } 105 | ``` 106 | 107 | ```rust 108 | fn main() { 109 | let s = String::from("hello, world"); 110 | print_str(&s); 111 | println!("{}", s); 112 | } 113 | fn print_str(s: &String) { 114 | println!("{}",s) 115 | } 116 | ``` 117 | 118 | 5. 119 | 120 | ```rust 121 | fn main() { 122 | let x = (1, 2, (), "hello"); 123 | let y = x; 124 | println!("{:?}, {:?}", x, y); 125 | } 126 | ``` 127 | 128 | 6. 129 | 130 | ```rust 131 | fn main() { 132 | let s = String::from("hello, "); 133 | 134 | // modify this line only ! 135 | let mut s1 = s; 136 | 137 | s1.push_str("world") 138 | } 139 | ``` 140 | 141 | 7. 142 | 143 | ```rust 144 | fn main() { 145 | let x = Box::new(5); 146 | 147 | let mut y = Box::new(3); // implement this line, dont change other lines! 148 | 149 | *y = 4; 150 | 151 | assert_eq!(*x, 5); 152 | } 153 | ``` 154 | 155 | 8. 156 | 157 | ```rust 158 | fn main() { 159 | let t = (String::from("hello"), String::from("world")); 160 | 161 | let _s = t.0; 162 | 163 | // modify this line only, don't use `_s` 164 | println!("{:?}", t.1); 165 | } 166 | ``` 167 | 168 | 9. 169 | 170 | ```rust 171 | fn main() { 172 | let t = (String::from("hello"), String::from("world")); 173 | 174 | // fill the blanks 175 | let (ref s1, ref s2) = t; 176 | 177 | println!("{:?}, {:?}, {:?}", s1, s2, t); // -> "hello", "world", ("hello", "world") 178 | } 179 | ``` 180 | 181 | ```rust 182 | fn main() { 183 | let t = (String::from("hello"), String::from("world")); 184 | 185 | // fill the blanks 186 | let (ref s1, ref s2) = t; 187 | 188 | println!("{:?}, {:?}, {:?}", s1, s2, t); // -> "hello", "world", ("hello", "world") 189 | } 190 | ``` 191 | -------------------------------------------------------------------------------- /solutions/pattern-match/patterns.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() {} 5 | fn match_number(n: i32) { 6 | match n { 7 | // match a single value 8 | 1 => println!("One!"), 9 | // fill in the blank with `|`, DON'T use `..` ofr `..=` 10 | 2 | 3 | 4 | 5 => println!("match 2 -> 5"), 11 | // match an inclusive range 12 | 6..=10 => { 13 | println!("match 6 -> 10") 14 | }, 15 | _ => { 16 | println!("match 11 -> +infinite") 17 | } 18 | } 19 | } 20 | ``` 21 | 22 | 2. 23 | 24 | ```rust 25 | 26 | struct Point { 27 | x: i32, 28 | y: i32, 29 | } 30 | 31 | fn main() { 32 | // fill in the blank to let p match the second arm 33 | let p = Point { x: 2, y: 20 }; // x can be [0, 5], y can be 10 20 or 30 34 | 35 | match p { 36 | Point { x, y: 0 } => println!("On the x axis at {}", x), 37 | // second arm 38 | Point { x: 0..=5, y: y@ (10 | 20 | 30) } => println!("On the y axis at {}", y), 39 | Point { x, y } => println!("On neither axis: ({}, {})", x, y), 40 | } 41 | } 42 | ``` 43 | 44 | 3. 45 | 46 | ```rust 47 | enum Message { 48 | Hello { id: i32 }, 49 | } 50 | 51 | fn main() { 52 | let msg = Message::Hello { id: 5 }; 53 | 54 | match msg { 55 | Message::Hello { 56 | id: id@3..=7, 57 | } => println!("Found an id in range [3, 7]: {}", id), 58 | Message::Hello { id: newid@(10 | 11 | 12) } => { 59 | println!("Found an id in another range [10, 12]: {}", newid) 60 | } 61 | Message::Hello { id } => println!("Found some other id: {}", id), 62 | } 63 | } 64 | ``` 65 | 66 | 4. 67 | 68 | ```rust 69 | fn main() { 70 | let num = Some(4); 71 | let split = 5; 72 | match num { 73 | Some(x) if x < split => assert!(x < split), 74 | Some(x) => assert!(x >= split), 75 | None => (), 76 | } 77 | } 78 | ``` 79 | 80 | 5. 81 | 82 | ```rust 83 | fn main() { 84 | let numbers = (2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048); 85 | 86 | match numbers { 87 | (first,..,last) => { 88 | assert_eq!(first, 2); 89 | assert_eq!(last, 2048); 90 | } 91 | } 92 | } 93 | ``` 94 | 95 | 6. 96 | 97 | ```rust 98 | fn main() { 99 | let mut v = String::from("hello,"); 100 | let r = &mut v; 101 | 102 | match r { 103 | // The type of value is &mut String 104 | value => value.push_str(" world!") 105 | } 106 | } 107 | ``` -------------------------------------------------------------------------------- /solutions/result-panic/panic.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | use core::panic; 5 | 6 | fn drink(beverage: &str) { 7 | if beverage == "lemonade" { 8 | println!("Success!"); 9 | // IMPLEMENT the below code 10 | panic!("drinked, duang.....peng!") 11 | } 12 | 13 | println!("Exercise Failed if printing out this line!"); 14 | } 15 | 16 | fn main() { 17 | drink("lemonade"); 18 | 19 | println!("Exercise Failed if printing out this line!"); 20 | } 21 | ``` 22 | 23 | 2. 24 | 25 | ```rust 26 | // MAKE the code work by fixing all panics 27 | fn main() { 28 | assert_eq!("abc".as_bytes(), [97, 98, 99]); 29 | 30 | let v = vec![1, 2, 3]; 31 | let ele = v[2]; 32 | // unwrap may panic when get return a None 33 | let ele = v.get(2).unwrap(); 34 | 35 | // Sometimes, the compiler is unable to find the overflow errors for you in compile time ,so a panic will occur 36 | let v = production_rate_per_hour(2); 37 | 38 | // because of the same reason as above, we have to wrap it in a function to make the panic occur 39 | divide(15, 1); 40 | 41 | println!("Success!") 42 | } 43 | 44 | fn divide(x:u8, y:u8) { 45 | println!("{}", x / y) 46 | } 47 | 48 | fn production_rate_per_hour(speed: u8) -> f64 { 49 | let cph: u8 = 21; 50 | match speed { 51 | 1..=4 => (speed * cph) as f64, 52 | 5..=8 => (speed * cph) as f64 * 0.9, 53 | 9..=10 => (speed * cph) as f64 * 0.77, 54 | _ => 0 as f64, 55 | } 56 | } 57 | 58 | pub fn working_items_per_minute(speed: u8) -> u32 { 59 | (production_rate_per_hour(speed) / 60 as f64) as u32 60 | } 61 | ``` 62 | -------------------------------------------------------------------------------- /solutions/type-conversions/as.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | let decimal = 97.123_f32; 6 | 7 | let integer: u8 = decimal as u8; 8 | 9 | let c1: char = decimal as u8 as char; 10 | let c2 = integer as char; 11 | 12 | assert_eq!(integer, 'b' as u8 - 1); 13 | } 14 | ``` 15 | 16 | 2. 17 | 18 | ```rust 19 | // Suppress all warnings from casts which overflow. 20 | #![allow(overflowing_literals)] 21 | 22 | fn main() { 23 | assert_eq!(u8::MAX, 255); 24 | let v = 1000 as u8; 25 | } 26 | ``` 27 | 28 | 3. 29 | 30 | ```rust 31 | fn main() { 32 | assert_eq!(1000 as u16, 1000); 33 | 34 | assert_eq!(1000 as u8, 232); 35 | 36 | // For positive numbers, this is the same as the modulus 37 | println!("1000 mod 256 is : {}", 1000 % 256); 38 | 39 | assert_eq!(-1_i8 as u8, 255); 40 | 41 | // Since Rust 1.45, the `as` keyword performs a *saturating cast* 42 | // when casting from float to int. If the floating point value exceeds 43 | // the upper bound or is less than the lower bound, the returned value 44 | // will be equal to the bound crossed. 45 | assert_eq!(300.1_f32 as u8, 255); 46 | assert_eq!(-100.1_f32 as u8, 0); 47 | 48 | 49 | // This behavior incurs a small runtime cost and can be avoided 50 | // with unsafe methods, however the results might overflow and 51 | // return **unsound values**. Use these methods wisely: 52 | unsafe { 53 | // 300.0 is 44 54 | println!("300.0 is {}", 300.0_f32.to_int_unchecked::()); 55 | // -100.0 as u8 is 156 56 | println!("-100.0 as u8 is {}", (-100.0_f32).to_int_unchecked::()); 57 | // nan as u8 is 0 58 | println!("nan as u8 is {}", f32::NAN.to_int_unchecked::()); 59 | } 60 | } 61 | ``` 62 | 63 | 4. 64 | 65 | ```rust 66 | fn main() { 67 | let mut values: [i32; 2] = [1, 2]; 68 | let p1: *mut i32 = values.as_mut_ptr(); 69 | let first_address = p1 as usize; 70 | let second_address = first_address + 4; // 4 == std::mem::size_of::() 71 | let p2 = second_address as *mut i32; 72 | unsafe { 73 | *p2 += 1; 74 | } 75 | assert_eq!(values[1], 3); 76 | 77 | println!("Success!") 78 | } 79 | ``` 80 | 81 | 5. 82 | 83 | ```rust 84 | fn main() { 85 | let arr :[u64; 13] = [0; 13]; 86 | assert_eq!(std::mem::size_of_val(&arr), 8 * 13); 87 | let a: *const [u64] = &arr; 88 | let b = a as *const [u8]; 89 | unsafe { 90 | assert_eq!(std::mem::size_of_val(&*b), 13) 91 | } 92 | } 93 | ``` -------------------------------------------------------------------------------- /solutions/type-conversions/from-into.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | // impl From for i32 6 | let i1: i32 = false.into(); 7 | let i2: i32 = i32::from(false); 8 | assert_eq!(i1, i2); 9 | assert_eq!(i1, 0); 10 | 11 | let i3: u32 = 'a'.into(); 12 | 13 | let s: String = 'a'.into(); 14 | } 15 | ``` 16 | 17 | ```rust 18 | fn main() { 19 | // impl From for i32 20 | let i1: i32 = false.into(); 21 | let i2: i32 = i32::from(false); 22 | assert_eq!(i1, i2); 23 | assert_eq!(i1, 0); 24 | 25 | let i3: i32 = 'a' as i32; 26 | 27 | let s: String = String::from('a'); 28 | } 29 | ``` 30 | 31 | 2. 32 | 33 | ```rust 34 | // From is now included in `std::prelude`, so there is no need to introduce it into the current scope 35 | // use std::convert::From; 36 | 37 | #[derive(Debug)] 38 | struct Number { 39 | value: i32, 40 | } 41 | 42 | impl From for Number { 43 | // IMPLEMENT `from` method 44 | fn from(item: i32) -> Self { 45 | Number { value: item } 46 | } 47 | } 48 | 49 | fn main() { 50 | let num = Number::from(30); 51 | assert_eq!(num.value, 30); 52 | 53 | let num: Number = 30.into(); 54 | assert_eq!(num.value, 30); 55 | 56 | println!("Success!") 57 | } 58 | ``` 59 | 60 | 3. 61 | 62 | ```rust 63 | use std::fs; 64 | use std::io; 65 | use std::num; 66 | 67 | enum CliError { 68 | IoError(io::Error), 69 | ParseError(num::ParseIntError), 70 | } 71 | 72 | impl From for CliError { 73 | fn from(error: io::Error) -> Self { 74 | CliError::IoError(error) 75 | } 76 | } 77 | 78 | impl From for CliError { 79 | fn from(error: num::ParseIntError) -> Self { 80 | CliError::ParseError(error) 81 | } 82 | } 83 | 84 | fn open_and_parse_file(file_name: &str) -> Result { 85 | // ? automatically converts io::Error to CliError 86 | let contents = fs::read_to_string(&file_name)?; 87 | // num::ParseIntError -> CliError 88 | let num: i32 = contents.trim().parse()?; 89 | Ok(num) 90 | } 91 | 92 | fn main() { 93 | println!("Success!") 94 | } 95 | ``` 96 | 97 | 4. 98 | 99 | ```rust 100 | fn main() { 101 | let n: i16 = 256; 102 | 103 | let n: u8 = match n.try_into() { 104 | Ok(n) => n, 105 | Err(e) => { 106 | println!("there is an error when converting: {:?}, but we catch it", e.to_string()); 107 | 0 108 | } 109 | }; 110 | 111 | assert_eq!(n, 0); 112 | 113 | println!("Success!") 114 | } 115 | ``` 116 | 117 | 5. 118 | 119 | ```rust,editable 120 | #[derive(Debug, PartialEq)] 121 | struct EvenNum(i32); 122 | 123 | impl TryFrom for EvenNum { 124 | type Error = (); 125 | 126 | fn try_from(value: i32) -> Result { 127 | if value % 2 == 0 { 128 | Ok(EvenNum(value)) 129 | } else { 130 | Err(()) 131 | } 132 | } 133 | } 134 | 135 | fn main() { 136 | assert_eq!(EvenNum::try_from(8), Ok(EvenNum(8))); 137 | assert_eq!(EvenNum::try_from(5), Err(())); 138 | 139 | let result: Result = 8i32.try_into(); 140 | assert_eq!(result, Ok(EvenNum(8))); 141 | let result: Result = 5i32.try_into(); 142 | assert_eq!(result, Err(())); 143 | } 144 | ``` 145 | -------------------------------------------------------------------------------- /solutions/type-conversions/others.md: -------------------------------------------------------------------------------- 1 | 1 2 | 3 | ```rust 4 | use std::fmt; 5 | 6 | struct Point { 7 | x: i32, 8 | y: i32, 9 | } 10 | 11 | impl fmt::Display for Point { 12 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 13 | write!(f, "The point is ({}, {})", self.x, self.y) 14 | } 15 | } 16 | 17 | fn main() { 18 | let origin = Point { x: 0, y: 0 }; 19 | assert_eq!(origin.to_string(), "The point is (0, 0)"); 20 | assert_eq!(format!("{}", origin), "The point is (0, 0)"); 21 | 22 | println!("Success!") 23 | } 24 | ``` 25 | 26 | 2. 27 | 28 | ```rust 29 | // To use `from_str` method, you needs to introduce this trait into the current scope. 30 | use std::str::FromStr; 31 | fn main() { 32 | let parsed: i32 = "5".parse().unwrap(); 33 | let turbo_parsed = "10".parse::().unwrap(); 34 | let from_str = i32::from_str("20").unwrap(); 35 | let sum = parsed + turbo_parsed + from_str; 36 | assert_eq!(sum, 35); 37 | 38 | println!("Success!") 39 | } 40 | ``` 41 | 42 | 3. 43 | 44 | ```rust 45 | use std::str::FromStr; 46 | use std::num::ParseIntError; 47 | 48 | #[derive(Debug, PartialEq)] 49 | struct Point { 50 | x: i32, 51 | y: i32 52 | } 53 | 54 | impl FromStr for Point { 55 | type Err = ParseIntError; 56 | 57 | fn from_str(s: &str) -> Result { 58 | let coords: Vec<&str> = s.trim_matches(|p| p == '(' || p == ')' ) 59 | .split(',') 60 | .collect(); 61 | 62 | let x_fromstr = coords[0].parse::()?; 63 | let y_fromstr = coords[1].parse::()?; 64 | 65 | Ok(Point { x: x_fromstr, y: y_fromstr }) 66 | } 67 | } 68 | fn main() { 69 | let p = "(3,4)".parse::(); 70 | assert_eq!(p.unwrap(), Point{ x: 3, y: 4} ) 71 | } 72 | ``` 73 | 74 | ```rust 75 | let p = Point::from_str("(3,4)"); 76 | ``` -------------------------------------------------------------------------------- /solutions/variables.md: -------------------------------------------------------------------------------- 1 | 1. 2 | 3 | ```rust 4 | fn main() { 5 | let x: i32 = 5; // uninitialized but using, ERROR ! 6 | let y: i32; // uninitialized but also unusing, only warning 7 | println!("{} is equal to 5", x); 8 | } 9 | ``` 10 | 11 | 2. 12 | 13 | ```rust 14 | fn main() { 15 | let mut x = 1; 16 | x += 2; 17 | 18 | println!("{} is equal to 3", x); 19 | } 20 | ``` 21 | 22 | 3. 23 | 24 | ```rust 25 | fn main() { 26 | let x: i32 = 10; 27 | let y: i32 = 20; 28 | { 29 | let y: i32 = 5; 30 | println!("The value of x is {} and value of y is {}", x, y); 31 | } 32 | println!("The value of x is {} and value of y is {}", x, y); 33 | } 34 | ``` 35 | 36 | 4. 37 | 38 | ```rust 39 | fn main() { 40 | define_x(); 41 | } 42 | 43 | fn define_x() { 44 | let x = "hello"; 45 | println!("{}, world", x); 46 | } 47 | ``` 48 | 49 | ```rust 50 | fn main() { 51 | let x = define_x(); 52 | println!("{}, world", x); 53 | } 54 | 55 | fn define_x() -> String { 56 | let x = "hello".to_string(); 57 | x 58 | } 59 | ``` 60 | 61 | ```rust 62 | fn main() { 63 | let x = define_x(); 64 | println!("{:?}, world", x); 65 | } 66 | 67 | fn define_x() -> &'static str { 68 | let x = "hello"; 69 | x 70 | } 71 | ``` 72 | 73 | 5. 74 | 75 | ```rust 76 | fn main() { 77 | let x: i32 = 5; 78 | { 79 | let x = 12; 80 | assert_eq!(x, 12); 81 | } 82 | 83 | assert_eq!(x, 5); 84 | 85 | let x = 42; 86 | println!("{}", x); // Prints "42". 87 | } 88 | ``` 89 | 90 | 6. 91 | 92 | ```rust 93 | fn main() { 94 | let mut x: i32 = 1; 95 | x = 7; 96 | // Shadowing and re-binding 97 | let x = x; 98 | 99 | 100 | let y = 4; 101 | // Shadowing 102 | let y = "I can also be bound to text!"; 103 | 104 | println!("Success!"); 105 | } 106 | ``` 107 | 108 | 7. 109 | 110 | ```rust 111 | fn main() { 112 | let _x = 1; 113 | } 114 | ``` 115 | 116 | ```rust 117 | #[allow(unused_variables)] 118 | fn main() { 119 | let x = 1; 120 | } 121 | ``` 122 | 123 | 8. 124 | 125 | ```rust 126 | fn main() { 127 | let (mut x, y) = (1, 2); 128 | x += 2; 129 | 130 | assert_eq!(x, 3); 131 | assert_eq!(y, 2); 132 | } 133 | ``` 134 | 135 | ```rust 136 | fn main() { 137 | let (x, y) = (1, 2); 138 | let x = 3; 139 | 140 | assert_eq!(x, 3); 141 | assert_eq!(y, 2); 142 | } 143 | ``` 144 | 145 | 9. 146 | 147 | ```rust 148 | fn main() { 149 | let (x, y); 150 | (x, ..) = (3, 4); 151 | [.., y] = [1, 2]; 152 | // fill the blank to make the code work 153 | assert_eq!([x, y], [3, 2]); 154 | } 155 | ``` 156 | -------------------------------------------------------------------------------- /zh-CN/assets/CNAME: -------------------------------------------------------------------------------- 1 | practice-zh.course.rs -------------------------------------------------------------------------------- /zh-CN/assets/lang1.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var path = window.location.pathname; 3 | var link = "https://practice.course.rs" + path; 4 | var word = "English"; 5 | var lang = "zh-CN"; 6 | var changeLang = "切换到英语"; 7 | 8 | if (window.location.href.indexOf("zh.") == -1) { 9 | link = "https://practice-zh.practice.rs" + path; 10 | word = "简体中文"; 11 | lang = "en"; 12 | changeLang = "Switch to Chinese"; 13 | } 14 | 15 | var lang_node = ""; 16 | if (link != "") { 17 | lang_node = 18 | ' ' + 25 | word + 26 | ""; 27 | } 28 | 29 | console.log(lang_node); 30 | var insertNode = document.getElementsByClassName("right-buttons"); 31 | if (insertNode.length > 0) { 32 | var html = insertNode[0].innerHTML; 33 | insertNode[0].innerHTML = html + lang_node; 34 | } 35 | })(); 36 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Build 17 | run: cargo build --verbose 18 | - name: Run tests 19 | run: cargo test --verbose 20 | - name: rustfmt 21 | uses: actions-rs/cargo@v1 22 | with: 23 | command: fmt 24 | args: --all -- --check 25 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Carl Lerche "] 3 | edition = "2018" 4 | name = "mini-redis" 5 | version = "0.4.1" 6 | license = "MIT" 7 | readme = "README.md" 8 | documentation = "https://docs.rs/mini-redis/0.4.0/mini-redis/" 9 | repository = "https://github.com/tokio-rs/mini-redis" 10 | description = """ 11 | An incomplete implementation of a Rust client and server. Used as a 12 | larger example of an idiomatic Tokio application. 13 | """ 14 | 15 | [[bin]] 16 | name = "mini-redis-cli" 17 | path = "src/bin/cli.rs" 18 | 19 | [[bin]] 20 | name = "mini-redis-server" 21 | path = "src/bin/server.rs" 22 | 23 | [dependencies] 24 | async-stream = "0.3.0" 25 | atoi = "0.3.2" 26 | bytes = "1" 27 | structopt = "0.3.14" 28 | tokio = { version = "1", features = ["full"] } 29 | tokio-stream = "0.1" 30 | tracing = "0.1.13" 31 | tracing-futures = { version = "0.2.3" } 32 | tracing-subscriber = "0.2.2" 33 | 34 | [dev-dependencies] 35 | # Enable test-utilities in dev mode only. This is mostly for tests. 36 | tokio = { version = "1", features = ["test-util"] } 37 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Tokio Contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/examples/chat.rs: -------------------------------------------------------------------------------- 1 | #[tokio::main] 2 | async fn main() { 3 | unimplemented!(); 4 | } 5 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/examples/hello_world.rs: -------------------------------------------------------------------------------- 1 | //! Hello world server. 2 | //! 3 | //! A simple client that connects to a mini-redis server, sets key "hello" with value "world", 4 | //! and gets it from the server after 5 | //! 6 | //! You can test this out by running: 7 | //! 8 | //! cargo run --bin mini-redis-server 9 | //! 10 | //! And then in another terminal run: 11 | //! 12 | //! cargo run --example hello_world 13 | 14 | #![warn(rust_2018_idioms)] 15 | 16 | use mini_redis::{client, Result}; 17 | 18 | #[tokio::main] 19 | pub async fn main() -> Result<()> { 20 | // Open a connection to the mini-redis address. 21 | let mut client = client::connect("127.0.0.1:6379").await?; 22 | 23 | // Set the key "hello" with value "world" 24 | client.set("hello", "world".into()).await?; 25 | 26 | // Get key "hello" 27 | let result = client.get("hello").await?; 28 | 29 | println!("got value from the server; success={:?}", result.is_some()); 30 | 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/examples/pub.rs: -------------------------------------------------------------------------------- 1 | //! Publish to a redis channel example. 2 | //! 3 | //! A simple client that connects to a mini-redis server, and 4 | //! publishes a message on `foo` channel 5 | //! 6 | //! You can test this out by running: 7 | //! 8 | //! cargo run --bin mini-redis-server 9 | //! 10 | //! Then in another terminal run: 11 | //! 12 | //! cargo run --example sub 13 | //! 14 | //! And then in another terminal run: 15 | //! 16 | //! cargo run --example pub 17 | 18 | #![warn(rust_2018_idioms)] 19 | 20 | use mini_redis::{client, Result}; 21 | 22 | #[tokio::main] 23 | async fn main() -> Result<()> { 24 | // Open a connection to the mini-redis address. 25 | let mut client = client::connect("127.0.0.1:6379").await?; 26 | 27 | // publish message `bar` on channel foo 28 | client.publish("foo", "bar".into()).await?; 29 | 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/examples/sub.rs: -------------------------------------------------------------------------------- 1 | //! Subscribe to a redis channel example. 2 | //! 3 | //! A simple client that connects to a mini-redis server, subscribes to "foo" and "bar" channels 4 | //! and awaits messages published on those channels 5 | //! 6 | //! You can test this out by running: 7 | //! 8 | //! cargo run --bin mini-redis-server 9 | //! 10 | //! Then in another terminal run: 11 | //! 12 | //! cargo run --example sub 13 | //! 14 | //! And then in another terminal run: 15 | //! 16 | //! cargo run --example pub 17 | 18 | 19 | 20 | use mini_redis::{client, Result}; 21 | use tokio_stream::StreamExt; 22 | #[tokio::main] 23 | pub async fn main() -> Result<()> { 24 | // Open a connection to the mini-redis address. 25 | let client = client::connect("127.0.0.1:6379").await?; 26 | 27 | // subscribe to channel foo 28 | let mut subscriber = client.subscribe(vec!["foo".into()]).await?; 29 | let messages = subscriber.into_stream(); 30 | tokio::pin!(messages); 31 | // await messages on channel foo 32 | while let Some(msg) = messages.next().await { 33 | println!("got = {:?}", msg); 34 | } 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/src/bin/cli.rs: -------------------------------------------------------------------------------- 1 | use mini_redis::{client, DEFAULT_PORT}; 2 | 3 | use bytes::Bytes; 4 | use std::num::ParseIntError; 5 | use std::str; 6 | use std::time::Duration; 7 | use structopt::StructOpt; 8 | 9 | #[derive(StructOpt, Debug)] 10 | #[structopt(name = "mini-redis-cli", author = env!("CARGO_PKG_AUTHORS"), about = "Issue Redis commands")] 11 | struct Cli { 12 | #[structopt(subcommand)] 13 | command: Command, 14 | 15 | #[structopt(name = "hostname", long = "--host", default_value = "127.0.0.1")] 16 | host: String, 17 | 18 | #[structopt(name = "port", long = "--port", default_value = DEFAULT_PORT)] 19 | port: String, 20 | } 21 | 22 | #[derive(StructOpt, Debug)] 23 | enum Command { 24 | /// Get the value of key. 25 | Get { 26 | /// Name of key to get 27 | key: String, 28 | }, 29 | /// Set key to hold the string value. 30 | Set { 31 | /// Name of key to set 32 | key: String, 33 | 34 | /// Value to set. 35 | #[structopt(parse(from_str = bytes_from_str))] 36 | value: Bytes, 37 | 38 | /// Expire the value after specified amount of time 39 | #[structopt(parse(try_from_str = duration_from_ms_str))] 40 | expires: Option, 41 | }, 42 | } 43 | 44 | /// Entry point for CLI tool. 45 | /// 46 | /// The `[tokio::main]` annotation signals that the Tokio runtime should be 47 | /// started when the function is called. The body of the function is executed 48 | /// within the newly spawned runtime. 49 | /// 50 | /// `flavor = "current_thread"` is used here to avoid spawning background 51 | /// threads. The CLI tool use case benefits more by being lighter instead of 52 | /// multi-threaded. 53 | #[tokio::main(flavor = "current_thread")] 54 | async fn main() -> mini_redis::Result<()> { 55 | // Enable logging 56 | tracing_subscriber::fmt::try_init()?; 57 | 58 | // Parse command line arguments 59 | let cli = Cli::from_args(); 60 | 61 | // Get the remote address to connect to 62 | let addr = format!("{}:{}", cli.host, cli.port); 63 | 64 | // Establish a connection 65 | let mut client = client::connect(&addr).await?; 66 | 67 | // Process the requested command 68 | match cli.command { 69 | Command::Get { key } => { 70 | if let Some(value) = client.get(&key).await? { 71 | if let Ok(string) = str::from_utf8(&value) { 72 | println!("\"{}\"", string); 73 | } else { 74 | println!("{:?}", value); 75 | } 76 | } else { 77 | println!("(nil)"); 78 | } 79 | } 80 | Command::Set { 81 | key, 82 | value, 83 | expires: None, 84 | } => { 85 | client.set(&key, value).await?; 86 | println!("OK"); 87 | } 88 | Command::Set { 89 | key, 90 | value, 91 | expires: Some(expires), 92 | } => { 93 | client.set_expires(&key, value, expires).await?; 94 | println!("OK"); 95 | } 96 | } 97 | 98 | Ok(()) 99 | } 100 | 101 | fn duration_from_ms_str(src: &str) -> Result { 102 | let ms = src.parse::()?; 103 | Ok(Duration::from_millis(ms)) 104 | } 105 | 106 | fn bytes_from_str(src: &str) -> Bytes { 107 | Bytes::from(src.to_string()) 108 | } 109 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/src/bin/server.rs: -------------------------------------------------------------------------------- 1 | //! mini-redis server. 2 | //! 3 | //! This file is the entry point for the server implemented in the library. It 4 | //! performs command line parsing and passes the arguments on to 5 | //! `mini_redis::server`. 6 | //! 7 | //! The `clap` crate is used for parsing arguments. 8 | 9 | use mini_redis::{server, DEFAULT_PORT}; 10 | 11 | use structopt::StructOpt; 12 | use tokio::net::TcpListener; 13 | use tokio::signal; 14 | 15 | #[tokio::main] 16 | pub async fn main() -> mini_redis::Result<()> { 17 | // enable logging 18 | // see https://docs.rs/tracing for more info 19 | tracing_subscriber::fmt::try_init()?; 20 | 21 | let cli = Cli::from_args(); 22 | let port = cli.port.as_deref().unwrap_or(DEFAULT_PORT); 23 | 24 | // Bind a TCP listener 25 | let listener = TcpListener::bind(&format!("127.0.0.1:{}", port)).await?; 26 | 27 | server::run(listener, signal::ctrl_c()).await; 28 | 29 | Ok(()) 30 | } 31 | 32 | #[derive(StructOpt, Debug)] 33 | #[structopt(name = "mini-redis-server", version = env!("CARGO_PKG_VERSION"), author = env!("CARGO_PKG_AUTHORS"), about = "A Redis server")] 34 | struct Cli { 35 | #[structopt(name = "port", long = "--port")] 36 | port: Option, 37 | } 38 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/src/cmd/get.rs: -------------------------------------------------------------------------------- 1 | use crate::{Connection, Db, Frame, Parse}; 2 | 3 | use bytes::Bytes; 4 | use tracing::{debug, instrument}; 5 | 6 | /// Get the value of key. 7 | /// 8 | /// If the key does not exist the special value nil is returned. An error is 9 | /// returned if the value stored at key is not a string, because GET only 10 | /// handles string values. 11 | #[derive(Debug)] 12 | pub struct Get { 13 | /// Name of the key to get 14 | key: String, 15 | } 16 | 17 | impl Get { 18 | /// Create a new `Get` command which fetches `key`. 19 | pub fn new(key: impl ToString) -> Get { 20 | Get { 21 | key: key.to_string(), 22 | } 23 | } 24 | 25 | /// Get the key 26 | pub fn key(&self) -> &str { 27 | &self.key 28 | } 29 | 30 | /// Parse a `Get` instance from a received frame. 31 | /// 32 | /// The `Parse` argument provides a cursor-like API to read fields from the 33 | /// `Frame`. At this point, the entire frame has already been received from 34 | /// the socket. 35 | /// 36 | /// The `GET` string has already been consumed. 37 | /// 38 | /// # Returns 39 | /// 40 | /// Returns the `Get` value on success. If the frame is malformed, `Err` is 41 | /// returned. 42 | /// 43 | /// # Format 44 | /// 45 | /// Expects an array frame containing two entries. 46 | /// 47 | /// ```text 48 | /// GET key 49 | /// ``` 50 | pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result { 51 | // The `GET` string has already been consumed. The next value is the 52 | // name of the key to get. If the next value is not a string or the 53 | // input is fully consumed, then an error is returned. 54 | let key = parse.next_string()?; 55 | 56 | Ok(Get { key }) 57 | } 58 | 59 | /// Apply the `Get` command to the specified `Db` instance. 60 | /// 61 | /// The response is written to `dst`. This is called by the server in order 62 | /// to execute a received command. 63 | #[instrument(skip(self, db, dst))] 64 | pub(crate) async fn apply(self, db: &Db, dst: &mut Connection) -> crate::Result<()> { 65 | // Get the value from the shared database state 66 | let response = if let Some(value) = db.get(&self.key) { 67 | // If a value is present, it is written to the client in "bulk" 68 | // format. 69 | Frame::Bulk(value) 70 | } else { 71 | // If there is no value, `Null` is written. 72 | Frame::Null 73 | }; 74 | 75 | debug!(?response); 76 | 77 | // Write the response back to the client 78 | dst.write_frame(&response).await?; 79 | 80 | Ok(()) 81 | } 82 | 83 | /// Converts the command into an equivalent `Frame`. 84 | /// 85 | /// This is called by the client when encoding a `Get` command to send to 86 | /// the server. 87 | pub(crate) fn into_frame(self) -> Frame { 88 | let mut frame = Frame::array(); 89 | frame.push_bulk(Bytes::from("get".as_bytes())); 90 | frame.push_bulk(Bytes::from(self.key.into_bytes())); 91 | frame 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/src/cmd/unknown.rs: -------------------------------------------------------------------------------- 1 | use crate::{Connection, Frame}; 2 | 3 | use tracing::{debug, instrument}; 4 | 5 | /// Represents an "unknown" command. This is not a real `Redis` command. 6 | #[derive(Debug)] 7 | pub struct Unknown { 8 | command_name: String, 9 | } 10 | 11 | impl Unknown { 12 | /// Create a new `Unknown` command which responds to unknown commands 13 | /// issued by clients 14 | pub(crate) fn new(key: impl ToString) -> Unknown { 15 | Unknown { 16 | command_name: key.to_string(), 17 | } 18 | } 19 | 20 | /// Returns the command name 21 | pub(crate) fn get_name(&self) -> &str { 22 | &self.command_name 23 | } 24 | 25 | /// Responds to the client, indicating the command is not recognized. 26 | /// 27 | /// This usually means the command is not yet implemented by `mini-redis`. 28 | #[instrument(skip(self, dst))] 29 | pub(crate) async fn apply(self, dst: &mut Connection) -> crate::Result<()> { 30 | let response = Frame::Error(format!("ERR unknown command '{}'", self.command_name)); 31 | 32 | debug!(?response); 33 | 34 | dst.write_frame(&response).await?; 35 | Ok(()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A minimal (i.e. very incomplete) implementation of a Redis server and 2 | //! client. 3 | //! 4 | //! The purpose of this project is to provide a larger example of an 5 | //! asynchronous Rust project built with Tokio. Do not attempt to run this in 6 | //! production... seriously. 7 | //! 8 | //! # Layout 9 | //! 10 | //! The library is structured such that it can be used with guides. There are 11 | //! modules that are public that probably would not be public in a "real" redis 12 | //! client library. 13 | //! 14 | //! The major components are: 15 | //! 16 | //! * `server`: Redis server implementation. Includes a single `run` function 17 | //! that takes a `TcpListener` and starts accepting redis client connections. 18 | //! 19 | //! * `client`: an asynchronous Redis client implementation. Demonstrates how to 20 | //! build clients with Tokio. 21 | //! 22 | //! * `cmd`: implementations of the supported Redis commands. 23 | //! 24 | //! * `frame`: represents a single Redis protocol frame. A frame is used as an 25 | //! intermediate representation between a "command" and the byte 26 | //! representation. 27 | 28 | pub mod blocking_client; 29 | pub mod client; 30 | 31 | pub mod cmd; 32 | pub use cmd::Command; 33 | 34 | mod connection; 35 | pub use connection::Connection; 36 | 37 | pub mod frame; 38 | pub use frame::Frame; 39 | 40 | mod db; 41 | use db::Db; 42 | use db::DbDropGuard; 43 | 44 | mod parse; 45 | use parse::{Parse, ParseError}; 46 | 47 | pub mod server; 48 | 49 | mod buffer; 50 | pub use buffer::{buffer, Buffer}; 51 | 52 | mod shutdown; 53 | use shutdown::Shutdown; 54 | 55 | /// Default port that a redis server listens on. 56 | /// 57 | /// Used if no port is specified. 58 | pub const DEFAULT_PORT: &str = "6379"; 59 | 60 | /// Error returned by most functions. 61 | /// 62 | /// When writing a real application, one might want to consider a specialized 63 | /// error handling crate or defining an error type as an `enum` of causes. 64 | /// However, for our example, using a boxed `std::error::Error` is sufficient. 65 | /// 66 | /// For performance reasons, boxing is avoided in any hot path. For example, in 67 | /// `parse`, a custom error `enum` is defined. This is because the error is hit 68 | /// and handled during normal execution when a partial frame is received on a 69 | /// socket. `std::error::Error` is implemented for `parse::Error` which allows 70 | /// it to be converted to `Box`. 71 | pub type Error = Box; 72 | 73 | /// A specialized `Result` type for mini-redis operations. 74 | /// 75 | /// This is defined as a convenience. 76 | pub type Result = std::result::Result; 77 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/src/shutdown.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::broadcast; 2 | 3 | /// Listens for the server shutdown signal. 4 | /// 5 | /// Shutdown is signalled using a `broadcast::Receiver`. Only a single value is 6 | /// ever sent. Once a value has been sent via the broadcast channel, the server 7 | /// should shutdown. 8 | /// 9 | /// The `Shutdown` struct listens for the signal and tracks that the signal has 10 | /// been received. Callers may query for whether the shutdown signal has been 11 | /// received or not. 12 | #[derive(Debug)] 13 | pub(crate) struct Shutdown { 14 | /// `true` if the shutdown signal has been received 15 | shutdown: bool, 16 | 17 | /// The receive half of the channel used to listen for shutdown. 18 | notify: broadcast::Receiver<()>, 19 | } 20 | 21 | impl Shutdown { 22 | /// Create a new `Shutdown` backed by the given `broadcast::Receiver`. 23 | pub(crate) fn new(notify: broadcast::Receiver<()>) -> Shutdown { 24 | Shutdown { 25 | shutdown: false, 26 | notify, 27 | } 28 | } 29 | 30 | /// Returns `true` if the shutdown signal has been received. 31 | pub(crate) fn is_shutdown(&self) -> bool { 32 | self.shutdown 33 | } 34 | 35 | /// Receive the shutdown notice, waiting if necessary. 36 | pub(crate) async fn recv(&mut self) { 37 | // If the shutdown signal has already been received, then return 38 | // immediately. 39 | if self.shutdown { 40 | return; 41 | } 42 | 43 | // Cannot receive a "lag error" as only one value is ever sent. 44 | let _ = self.notify.recv().await; 45 | 46 | // Remember that the signal has been received. 47 | self.shutdown = true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/tests/buffer.rs: -------------------------------------------------------------------------------- 1 | use mini_redis::{buffer, client, server}; 2 | use std::net::SocketAddr; 3 | use tokio::net::TcpListener; 4 | use tokio::task::JoinHandle; 5 | 6 | /// A basic "hello world" style test. A server instance is started in a 7 | /// background task. A client instance is then established and used to intialize 8 | /// the buffer. Set and get commands are sent to the server. The response is 9 | /// then evaluated. 10 | #[tokio::test] 11 | async fn pool_key_value_get_set() { 12 | let (addr, _) = start_server().await; 13 | 14 | let client = client::connect(addr).await.unwrap(); 15 | let mut client = buffer(client); 16 | 17 | client.set("hello", "world".into()).await.unwrap(); 18 | 19 | let value = client.get("hello").await.unwrap().unwrap(); 20 | assert_eq!(b"world", &value[..]) 21 | } 22 | 23 | async fn start_server() -> (SocketAddr, JoinHandle<()>) { 24 | let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); 25 | let addr = listener.local_addr().unwrap(); 26 | 27 | let handle = tokio::spawn(async move { server::run(listener, tokio::signal::ctrl_c()).await }); 28 | 29 | (addr, handle) 30 | } 31 | -------------------------------------------------------------------------------- /zh-CN/assets/mini-redis/tests/client.rs: -------------------------------------------------------------------------------- 1 | use mini_redis::{client, server}; 2 | use std::net::SocketAddr; 3 | use tokio::net::TcpListener; 4 | use tokio::task::JoinHandle; 5 | 6 | /// A basic "hello world" style test. A server instance is started in a 7 | /// background task. A client instance is then established and set and get 8 | /// commands are sent to the server. The response is then evaluated 9 | #[tokio::test] 10 | async fn key_value_get_set() { 11 | let (addr, _) = start_server().await; 12 | 13 | let mut client = client::connect(addr).await.unwrap(); 14 | client.set("hello", "world".into()).await.unwrap(); 15 | 16 | let value = client.get("hello").await.unwrap().unwrap(); 17 | assert_eq!(b"world", &value[..]) 18 | } 19 | 20 | /// similar to the "hello world" style test, But this time 21 | /// a single channel subscription will be tested instead 22 | #[tokio::test] 23 | async fn receive_message_subscribed_channel() { 24 | let (addr, _) = start_server().await; 25 | 26 | let client = client::connect(addr.clone()).await.unwrap(); 27 | let mut subscriber = client.subscribe(vec!["hello".into()]).await.unwrap(); 28 | 29 | tokio::spawn(async move { 30 | let mut client = client::connect(addr).await.unwrap(); 31 | client.publish("hello", "world".into()).await.unwrap() 32 | }); 33 | 34 | let message = subscriber.next_message().await.unwrap().unwrap(); 35 | assert_eq!("hello", &message.channel); 36 | assert_eq!(b"world", &message.content[..]) 37 | } 38 | 39 | /// test that a client gets messages from multiple subscribed channels 40 | #[tokio::test] 41 | async fn receive_message_multiple_subscribed_channels() { 42 | let (addr, _) = start_server().await; 43 | 44 | let client = client::connect(addr.clone()).await.unwrap(); 45 | let mut subscriber = client 46 | .subscribe(vec!["hello".into(), "world".into()]) 47 | .await 48 | .unwrap(); 49 | 50 | tokio::spawn(async move { 51 | let mut client = client::connect(addr).await.unwrap(); 52 | client.publish("hello", "world".into()).await.unwrap() 53 | }); 54 | 55 | let message1 = subscriber.next_message().await.unwrap().unwrap(); 56 | assert_eq!("hello", &message1.channel); 57 | assert_eq!(b"world", &message1.content[..]); 58 | 59 | tokio::spawn(async move { 60 | let mut client = client::connect(addr).await.unwrap(); 61 | client.publish("world", "howdy?".into()).await.unwrap() 62 | }); 63 | 64 | let message2 = subscriber.next_message().await.unwrap().unwrap(); 65 | assert_eq!("world", &message2.channel); 66 | assert_eq!(b"howdy?", &message2.content[..]) 67 | } 68 | 69 | /// test that a client accurately removes its own subscribed chanel list 70 | /// when unbscribing to all subscribed channels by submitting an empty vec 71 | #[tokio::test] 72 | async fn unsubscribes_from_channels() { 73 | let (addr, _) = start_server().await; 74 | 75 | let client = client::connect(addr.clone()).await.unwrap(); 76 | let mut subscriber = client 77 | .subscribe(vec!["hello".into(), "world".into()]) 78 | .await 79 | .unwrap(); 80 | 81 | subscriber.unsubscribe(&[]).await.unwrap(); 82 | assert_eq!(subscriber.get_subscribed().len(), 0); 83 | } 84 | 85 | async fn start_server() -> (SocketAddr, JoinHandle<()>) { 86 | let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); 87 | let addr = listener.local_addr().unwrap(); 88 | 89 | let handle = tokio::spawn(async move { server::run(listener, tokio::signal::ctrl_c()).await }); 90 | 91 | (addr, handle) 92 | } 93 | -------------------------------------------------------------------------------- /zh-CN/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Rust By Practice( Rust 练习实践 )" 3 | description = "Learning Rust By Practice, narrowing the gap between beginner and skilled-dev with challenging examples, exercises and projects." 4 | authors = ["sunface, https://im.dev"] 5 | language = "en" 6 | 7 | [output.html.playpen] 8 | editable = true 9 | editor = "ace" 10 | 11 | [output.html.fold] 12 | enable = true 13 | level = 1 14 | 15 | [output.html] 16 | additional-css = ["theme/style1.css"] 17 | additional-js = ["assets/custom3.js", "assets/lang1.js"] 18 | git-repository-url = "https://github.com/sunface/rust-by-practice" 19 | edit-url-template = "https://github.com/sunface/rust-by-practice/edit/master/zh-CN/{path}" 20 | 21 | [rust] 22 | edition = "2018" 23 | -------------------------------------------------------------------------------- /zh-CN/deploy.sh: -------------------------------------------------------------------------------- 1 | ## this script deploys the static website of course.rs to github pages 2 | 3 | ## build static website for book 4 | mdbook build 5 | ## copy CNAME info to book dir 6 | cp ./assets/CNAME ./book/ 7 | cp ./assets/*.html ./book/ 8 | cp ./assets/sitemap.xml ./book/ 9 | 10 | ## init git repo 11 | cd book 12 | git init 13 | git config user.name "sunface" 14 | git config user.email "cto@188.com" 15 | git add . 16 | git commit -m 'deploy' 17 | git branch -M gh-pages 18 | git remote add origin https://github.com/sunface/zh.practice.rs 19 | 20 | ## push to github pages 21 | git push -u -f origin gh-pages -------------------------------------------------------------------------------- /zh-CN/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [关于 practice.rs](why-exercise.md) 4 | - [值得学习的小型项目](elegant-code-base.md) 5 | - [变量绑定与解构](variables.md) 6 | - [基本类型](basic-types/intro.md) 7 | - [数值类型](basic-types/numbers.md) 8 | - [字符、布尔、单元类型](basic-types/char-bool-unit.md) 9 | - [语句与表达式](basic-types/statements-expressions.md) 10 | - [函数](basic-types/functions.md) 11 | - [所有权和借用](ownership/intro.md) 12 | - [所有权](ownership/ownership.md) 13 | - [引用和借用](ownership/borrowing.md) 14 | - [复合类型](compound-types/intro.md) 15 | - [字符串](compound-types/string.md) 16 | - [数组](compound-types/array.md) 17 | - [切片](compound-types/slice.md) 18 | - [元组](compound-types/tuple.md) 19 | - [结构体](compound-types/struct.md) 20 | - [枚举](compound-types/enum.md) 21 | - [流程控制](flow-control.md) 22 | - [模式匹配](pattern-match/intro.md) 23 | - [match, matches! 和 if let](pattern-match/match-iflet.md) 24 | - [模式](pattern-match/patterns.md) 25 | - [方法和关联函数](method.md) 26 | - [泛型和特征](generics-traits/intro.md) 27 | - [泛型](generics-traits/generics.md) 28 | - [Const 泛型](generics-traits/const-generics.md) 29 | - [特征 Traits](generics-traits/traits.md) 30 | - [特征对象](generics-traits/trait-object.md) 31 | - [进一步深入特征](generics-traits/advanced-traits.md) 32 | - [集合类型](collections/intro.md) 33 | - [动态字符串 String](collections/string.md) 34 | - [动态数组 Vector](collections/vector.md) 35 | - [KV 存储 HashMap](collections/hashmap.md) 36 | - [类型转换](type-conversions/intro.md) 37 | - [as](type-conversions/as.md) 38 | - [From/Into](type-conversions/from-into.md) 39 | - [其它转换](type-conversions/others.md) 40 | - [返回值和 panic!](result-panic/intro.md) 41 | - [panic! 深入剖析](result-panic/panic.md) 42 | - [返回值result 和 ?](result-panic/result.md) 43 | - [包和模块](crate-module/intro.md) 44 | - [包 Crate](crate-module/crate.md) 45 | - [模块 Module](crate-module/module.md) 46 | - [使用use引入模块及受限可见性](crate-module/use-pub.md) 47 | - [注释和文档](comments-docs.md) 48 | - [格式化输出](formatted-output.md) 49 | - [生命周期](lifetime/intro.md) 50 | - [生命周期基础](lifetime/basic.md) 51 | - [&'static 和 T: 'static](lifetime/static.md) 52 | - [深入生命周期](lifetime/advance.md) 53 | - [函数式编程: 闭包、迭代器 todo](functional-programing/intro.md) 54 | - [闭包 Closure](functional-programing/closure.md) 55 | - [迭代器 Iterator](functional-programing/iterator.md) 56 | - [newtype 和 Sized todo](newtype-sized.md) 57 | - [智能指针 todo](smart-pointers/intro.md) 58 | - [Box](smart-pointers/box.md) 59 | - [Deref](smart-pointers/deref.md) 60 | - [Drop](smart-pointers/drop.md) 61 | - [Rc and Arc](smart-pointers/rc-arc.md) 62 | - [Cell and RefCell](smart-pointers/cell-refcell.md) 63 | - [Weak 和循环引用todo](weak.md) 64 | - [自引用 todo](self-referential.md) 65 | - [多线程 todo](threads/intro.md) 66 | - [多线程基础](threads/basic-using.md) 67 | - [消息传递](threads/message-passing.md) 68 | - [线程同步:锁、Condvar和信号量](threads/sync.md) 69 | - [线程同步:Atomic](threads/atomic.md) 70 | - [Send 和 Sync](threads/send-sync.md) 71 | - [全局变量 todo](global-variables.md) 72 | - [错误处理 todo](errors.md) 73 | - [Unsafe doing](unsafe/intro.md) 74 | - [内联汇编](unsafe/inline-asm.md) 75 | - [macro 宏 todo](macro.md) 76 | - [测试 todo](tests/intro.md) 77 | - [编写测试及控制执行](tests/write-tests.md) 78 | - [基准性能测试 Benchmark](tests/benchmark.md) 79 | - [单元测试及集成测试](tests/unit-integration.md) 80 | - [断言 Assertions](tests/assertions.md) 81 | - [Async/Await 异步编程 todo](async/intro.md) 82 | - [async 和 await!](async/async-await.md) 83 | - [Future](async/future.md) 84 | - [Pin 和 Unpin](async/pin-unpin.md) 85 | - [Stream 流处理](async/stream.md) 86 | -------------------------------------------------------------------------------- /zh-CN/src/about.md: -------------------------------------------------------------------------------- 1 | # 关于 practice.rs 2 | -------------------------------------------------------------------------------- /zh-CN/src/async/async-await.md: -------------------------------------------------------------------------------- 1 | # async and await! 2 | -------------------------------------------------------------------------------- /zh-CN/src/async/future.md: -------------------------------------------------------------------------------- 1 | # Future 2 | -------------------------------------------------------------------------------- /zh-CN/src/async/intro.md: -------------------------------------------------------------------------------- 1 | # Async/Await 2 | -------------------------------------------------------------------------------- /zh-CN/src/async/pin-unpin.md: -------------------------------------------------------------------------------- 1 | # Pin and Unpin 2 | -------------------------------------------------------------------------------- /zh-CN/src/async/stream.md: -------------------------------------------------------------------------------- 1 | # Stream 2 | -------------------------------------------------------------------------------- /zh-CN/src/basic-types/char-bool-unit.md: -------------------------------------------------------------------------------- 1 | # 字符、布尔、单元类型 2 | 3 | ### 字符 4 | 1. 🌟 5 | ```rust,editable 6 | // 修改2处 `assert_eq!` 让代码工作 7 | 8 | use std::mem::size_of_val; 9 | fn main() { 10 | let c1 = 'a'; 11 | assert_eq!(size_of_val(&c1),1); 12 | 13 | let c2 = '中'; 14 | assert_eq!(size_of_val(&c2),3); 15 | 16 | println!("Success!") 17 | } 18 | ``` 19 | 20 | 2. 🌟 21 | ```rust,editable 22 | // 修改一行让代码正常打印 23 | fn main() { 24 | let c1 = "中"; 25 | print_char(c1); 26 | } 27 | 28 | fn print_char(c : char) { 29 | println!("{}", c); 30 | } 31 | ``` 32 | 33 | ### 布尔 34 | 3. 🌟 35 | ```rust,editable 36 | 37 | // 使成功打印 38 | fn main() { 39 | let _f: bool = false; 40 | 41 | let t = true; 42 | if !t { 43 | println!("Success!") 44 | } 45 | } 46 | ``` 47 | 48 | 4. 🌟 49 | ```rust,editable 50 | 51 | fn main() { 52 | let f = true; 53 | let t = true && false; 54 | assert_eq!(t, f); 55 | 56 | println!("Success!") 57 | } 58 | ``` 59 | 60 | 61 | ### 单元类型 62 | 5. 🌟🌟 63 | ```rust,editable 64 | 65 | // 让代码工作,但不要修改 `implicitly_ret_unit` ! 66 | fn main() { 67 | let _v: () = (); 68 | 69 | let v = (2, 3); 70 | assert_eq!(v, implicitly_ret_unit()); 71 | 72 | println!("Success!") 73 | } 74 | 75 | fn implicitly_ret_unit() { 76 | println!("I will return a ()") 77 | } 78 | 79 | // 不要使用下面的函数,它只用于演示! 80 | fn explicitly_ret_unit() -> () { 81 | println!("I will return a ()") 82 | } 83 | ``` 84 | 85 | 6. 🌟🌟 单元类型占用的内存大小是多少? 86 | ```rust,editable 87 | 88 | // 让代码工作:修改 `assert!` 中的 `4` 89 | use std::mem::size_of_val; 90 | fn main() { 91 | let unit: () = (); 92 | assert!(size_of_val(&unit) == 4); 93 | 94 | println!("Success!") 95 | } 96 | ``` 97 | 98 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/basic-types/char-bool.md)找到答案(在 solutions 路径下) 99 | -------------------------------------------------------------------------------- /zh-CN/src/basic-types/functions.md: -------------------------------------------------------------------------------- 1 | # 函数 2 | 1. 🌟🌟🌟 3 | ```rust,editable 4 | 5 | fn main() { 6 | // 不要修改下面两行代码! 7 | let (x, y) = (1, 2); 8 | let s = sum(x, y); 9 | 10 | assert_eq!(s, 3); 11 | } 12 | 13 | fn sum(x, y: i32) { 14 | x + y; 15 | } 16 | ``` 17 | 18 | 19 | 2. 🌟🌟 20 | ```rust,editable 21 | fn main() { 22 | print(); 23 | } 24 | 25 | // 使用另一个类型来替代 i32 26 | fn print() -> i32 { 27 | println!("hello,world"); 28 | } 29 | ``` 30 | 31 | 32 | 3. 🌟🌟🌟 33 | 34 | ```rust,editable 35 | // 用两种方法求解 36 | fn main() { 37 | never_return(); 38 | } 39 | 40 | fn never_return() -> ! { 41 | // 实现这个函数,不要修改函数签名! 42 | 43 | } 44 | ``` 45 | 46 | 4. 🌟🌟 发散函数( Diverging function )不会返回任何值,因此它们可以用于替代需要返回任何值的地方 47 | ```rust,editable 48 | 49 | fn main() { 50 | println!("Success!"); 51 | } 52 | 53 | fn get_option(tp: u8) -> Option { 54 | match tp { 55 | 1 => { 56 | // TODO 57 | } 58 | _ => { 59 | // TODO 60 | } 61 | }; 62 | 63 | // 这里与其返回一个 None,不如使用发散函数替代 64 | never_return_fn() 65 | } 66 | 67 | // 使用三种方法实现以下发散函数 68 | fn never_return_fn() -> ! { 69 | 70 | } 71 | ``` 72 | 73 | 5. 🌟🌟 74 | ```rust,editable 75 | 76 | fn main() { 77 | // 填空 78 | let b = __; 79 | 80 | let _v = match b { 81 | true => 1, 82 | // 发散函数也可以用于 `match` 表达式,用于替代任何类型的值 83 | false => { 84 | println!("Success!"); 85 | panic!("we have no value for `false`, but we can panic") 86 | } 87 | }; 88 | 89 | println!("Exercise Failed if printing out this line!"); 90 | } 91 | ``` 92 | 93 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/basic-types/functions.md)找到答案(在 solutions 路径下) 94 | -------------------------------------------------------------------------------- /zh-CN/src/basic-types/intro.md: -------------------------------------------------------------------------------- 1 | # 基本类型 2 | 学习资料: 3 | - English: [Rust Book 3.2 and 3.3](https://doc.rust-lang.org/book/ch03-02-data-types.html) 4 | - 简体中文: [Rust语言圣经 - 基本类型](https://course.rs/basic/base-type/index.html) 5 | 6 | -------------------------------------------------------------------------------- /zh-CN/src/basic-types/numbers.md: -------------------------------------------------------------------------------- 1 | # 数值类型 2 | 3 | ### 整数 4 | 5 | 1. 🌟 6 | 7 | > Tips: 如果我们没有显式的给予变量一个类型,那编译器会自动帮我们推导一个类型 8 | 9 | ```rust,editable 10 | 11 | // 移除某个部分让代码工作 12 | fn main() { 13 | let x: i32 = 5; 14 | let mut y: u32 = 5; 15 | 16 | y = x; 17 | 18 | let z = 10; // 这里 z 的类型是? 19 | } 20 | ``` 21 | 22 | 2. 🌟 23 | ```rust,editable 24 | 25 | // 填空 26 | fn main() { 27 | let v: u16 = 38_u8 as __; 28 | } 29 | ``` 30 | 31 | 3. 🌟🌟🌟 32 | 33 | > Tips: 如果我们没有显式的给予变量一个类型,那编译器会自动帮我们推导一个类型 34 | 35 | ```rust,editable 36 | 37 | // 修改 `assert_eq!` 让代码工作 38 | fn main() { 39 | let x = 5; 40 | assert_eq!("u32".to_string(), type_of(&x)); 41 | } 42 | 43 | // 以下函数可以获取传入参数的类型,并返回类型的字符串形式,例如 "i8", "u8", "i32", "u32" 44 | fn type_of(_: &T) -> String { 45 | format!("{}", std::any::type_name::()) 46 | } 47 | ``` 48 | 49 | 4. 🌟🌟 50 | ```rust,editable 51 | 52 | // 填空,让代码工作 53 | fn main() { 54 | assert_eq!(i8::MAX, __); 55 | assert_eq!(u8::MAX, __); 56 | } 57 | ``` 58 | 59 | 5. 🌟🌟 60 | ```rust,editable 61 | 62 | // 解决代码中的错误和 `panic` 63 | fn main() { 64 | let v1 = 251_u8 + 8; 65 | let v2 = i8::checked_add(251, 8).unwrap(); 66 | println!("{},{}",v1,v2); 67 | } 68 | ``` 69 | 70 | 6. 🌟🌟 71 | ```rust,editable 72 | 73 | // 修改 `assert!` 让代码工作 74 | fn main() { 75 | let v = 1_024 + 0xff + 0o77 + 0b1111_1111; 76 | assert!(v == 1579); 77 | } 78 | ``` 79 | 80 | 81 | ### 浮点数 82 | 7. 🌟 83 | 84 | ```rust,editable 85 | 86 | // 将 ? 替换成你的答案 87 | fn main() { 88 | let x = 1_000.000_1; // ? 89 | let y: f32 = 0.12; // f32 90 | let z = 0.01_f64; // f64 91 | } 92 | ``` 93 | 8. 🌟🌟 使用两种方法来让下面代码工作 94 | 95 | 96 | ```rust,editable 97 | 98 | fn main() { 99 | assert!(0.1+0.2==0.3); 100 | } 101 | ``` 102 | 103 | ### 序列Range 104 | 9. 🌟🌟 两个目标: 1. 修改 `assert!` 让它工作 2. 让 `println!` 输出: 97 - 122 105 | 106 | ```rust,editable 107 | fn main() { 108 | let mut sum = 0; 109 | for i in -3..2 { 110 | sum += i 111 | } 112 | 113 | assert!(sum == -3); 114 | 115 | for c in 'a'..='z' { 116 | println!("{}",c); 117 | } 118 | } 119 | ``` 120 | 121 | 10. 🌟🌟 122 | ```rust,editable 123 | 124 | // 填空 125 | use std::ops::{Range, RangeInclusive}; 126 | fn main() { 127 | assert_eq!((1..__), Range{ start: 1, end: 5 }); 128 | assert_eq!((1..__), RangeInclusive::new(1, 5)); 129 | } 130 | ``` 131 | 132 | ### 计算 133 | 134 | 11. 🌟 135 | ```rust,editable 136 | 137 | // 填空,并解决错误 138 | fn main() { 139 | // 整数加法 140 | assert!(1u32 + 2 == __); 141 | 142 | // 整数减法 143 | assert!(1i32 - 2 == __); 144 | assert!(1u8 - 2 == -1); 145 | 146 | assert!(3 * 50 == __); 147 | 148 | assert!(9.6 / 3.2 == 3.0); // error ! 修改它让代码工作 149 | 150 | assert!(24 % 5 == __); 151 | 152 | // 逻辑与或非操作 153 | assert!(true && false == __); 154 | assert!(true || false == __); 155 | assert!(!true == __); 156 | 157 | // 位操作 158 | println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101); 159 | println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101); 160 | println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101); 161 | println!("1 << 5 is {}", 1u32 << 5); 162 | println!("0x80 >> 2 is 0x{:x}", 0x80u32 >> 2); 163 | } 164 | ``` 165 | 166 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/basic-types/numbers.md)找到答案(在 solutions 路径下) 167 | -------------------------------------------------------------------------------- /zh-CN/src/basic-types/statements-expressions.md: -------------------------------------------------------------------------------- 1 | # 语句与表达式 2 | 3 | ### 示例 4 | ```rust,editable 5 | fn main() { 6 | let x = 5u32; 7 | 8 | let y = { 9 | let x_squared = x * x; 10 | let x_cube = x_squared * x; 11 | 12 | // 下面表达式的值将被赋给 `y` 13 | x_cube + x_squared + x 14 | }; 15 | 16 | let z = { 17 | // 分号让表达式变成了语句,因此返回的不再是表达式 `2 * x` 的值,而是语句的值 `()` 18 | 2 * x; 19 | }; 20 | 21 | println!("x is {:?}", x); 22 | println!("y is {:?}", y); 23 | println!("z is {:?}", z); 24 | } 25 | ``` 26 | 27 | ### 练习 28 | 1. 🌟🌟 29 | ```rust,editable 30 | // 使用两种方法让代码工作起来 31 | fn main() { 32 | let v = { 33 | let mut x = 1; 34 | x += 2 35 | }; 36 | 37 | assert_eq!(v, 3); 38 | } 39 | ``` 40 | 41 | 2. 🌟 42 | ```rust,editable 43 | 44 | fn main() { 45 | let v = (let x = 3); 46 | 47 | assert!(v == 3); 48 | } 49 | ``` 50 | 51 | 3. 🌟 52 | ```rust,editable 53 | 54 | fn main() { 55 | let s = sum(1 , 2); 56 | assert_eq!(s, 3); 57 | } 58 | 59 | fn sum(x: i32, y: i32) -> i32 { 60 | x + y; 61 | } 62 | ``` 63 | 64 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/basic-types/statements.md)找到答案(在 solutions 路径下) 65 | -------------------------------------------------------------------------------- /zh-CN/src/circle-reference/intro.md: -------------------------------------------------------------------------------- 1 | # Circle reference and Self referential 2 | -------------------------------------------------------------------------------- /zh-CN/src/collections/intro.md: -------------------------------------------------------------------------------- 1 | # 集合类型 2 | 学习资源: 3 | - 简体中文: [Rust语言圣经 - 集合类型](https://course.rs/basic/collections/intro.html) 4 | 5 | -------------------------------------------------------------------------------- /zh-CN/src/compound-types/array.md: -------------------------------------------------------------------------------- 1 | # 数组 2 | 数组的类型是 `[T; Length]`,就如你所看到的,数组的长度是类型签名的一部分,因此数组的长度必须在编译期就已知,例如你不能使用以下方式来声明一个数组: 3 | ```rust 4 | fn create_arr(n: i32) { 5 | let arr = [1; n]; 6 | } 7 | ``` 8 | 9 | 以上函数将报错,因为编译器无法在编译期知道 `n` 的具体大小。 10 | 11 | 1. 🌟 12 | ```rust,editable 13 | 14 | fn main() { 15 | // 使用合适的类型填空 16 | let arr: __ = [1, 2, 3, 4, 5]; 17 | 18 | // 修改以下代码,让它顺利运行 19 | assert!(arr.len() == 4); 20 | } 21 | ``` 22 | 23 | 2. 🌟🌟 24 | ```rust,editable 25 | 26 | fn main() { 27 | // 很多时候,我们可以忽略数组的部分类型,也可以忽略全部类型,让编译器帮助我们推导 28 | let arr0 = [1, 2, 3]; 29 | let arr: [_; 3] = ['a', 'b', 'c']; 30 | 31 | // 填空 32 | // 数组分配在栈上, `std::mem::size_of_val` 函数会返回整个数组占用的内存空间 33 | // 数组中的每个 char 元素占用 4 字节的内存空间,因为在 Rust 中, char 是 Unicode 字符 34 | assert!(std::mem::size_of_val(&arr) == __); 35 | } 36 | ``` 37 | 38 | 3. 🌟 数组中的所有元素可以一起初始化为同一个值 39 | ```rust,editable 40 | 41 | fn main() { 42 | // 填空 43 | let list: [i32; 100] = __ ; 44 | 45 | assert!(list[0] == 1); 46 | assert!(list.len() == 100); 47 | } 48 | ``` 49 | 50 | 4. 🌟 数组中的所有元素必须是同一类型 51 | ```rust,editable 52 | 53 | fn main() { 54 | // 修复错误 55 | let _arr = [1, 2, '3']; 56 | } 57 | ``` 58 | 59 | 5. 🌟 数组的下标索引从 0 开始. 60 | ```rust,editable 61 | 62 | fn main() { 63 | let arr = ['a', 'b', 'c']; 64 | 65 | let ele = arr[1]; // 只修改此行来让代码工作 66 | 67 | assert!(ele == 'a'); 68 | } 69 | ``` 70 | 71 | 6. 🌟 越界索引会导致代码的 `panic`. 72 | ```rust,editable 73 | 74 | // 修复代码中的错误 75 | fn main() { 76 | let names = [String::from("Sunfei"), "Sunface".to_string()]; 77 | 78 | // `get` 返回 `Option` 类型,因此它的使用非常安全 79 | let name0 = names.get(0).unwrap(); 80 | 81 | // 但是下标索引就存在越界的风险了 82 | let _name1 = &names[2]; 83 | } 84 | ``` 85 | 86 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/compound-types/array.md)找到答案(在 solutions 路径下) -------------------------------------------------------------------------------- /zh-CN/src/compound-types/intro.md: -------------------------------------------------------------------------------- 1 | # 复合类型 2 | 学习资料: 3 | - English: [Rust Book 4.3, 5.1, 6.1, 8.2](https://doc.rust-lang.org/book/ch04-03-slices.html) 4 | - 简体中文: [Rust语言圣经 - 复合类型](https://course.rs/basic/compound-type/intro.html) 5 | 6 | -------------------------------------------------------------------------------- /zh-CN/src/compound-types/slice.md: -------------------------------------------------------------------------------- 1 | # 切片( Slice ) 2 | 切片跟数组相似,但是切片的长度无法在编译期得知,因此你无法直接使用切片类型。 3 | 4 | 1. 🌟🌟 这里, `[i32]` 和 `str` 都是切片类型,但是直接使用它们会造成编译错误,如下代码所示。为了解决,你需要使用切片的引用: `&[i32]`,`&str`。 5 | ```rust,editable 6 | 7 | // 修复代码中的错误,不要新增代码行! 8 | fn main() { 9 | let arr = [1, 2, 3]; 10 | let s1: [i32] = arr[0..2]; 11 | 12 | let s2: str = "hello, world" as str; 13 | } 14 | ``` 15 | 16 | 一个切片引用占用了2个字大小的内存空间( 从现在开始,为了简洁性考虑,如无特殊原因,**我们统一使用切片来特指切片引用** )。 该切片的第一个字是指向数据的指针,第二个字是切片的长度。字的大小取决于处理器架构,例如在 `x86-64` 上,字的大小是 64 位也就是 8 个字节,那么一个切片引用就是 16 个字节大小。 17 | 18 | 切片( 引用 )可以用来借用数组的某个连续的部分,对应的签名是 `&[T]`,大家可以与数组的签名对比下 `[T; Length]`。 19 | 20 | 2. 🌟🌟🌟 21 | ```rust,editable 22 | 23 | fn main() { 24 | let arr: [char; 3] = ['中', '国', '人']; 25 | 26 | let slice = &arr[..2]; 27 | 28 | // 修改数字 `8` 让代码工作 29 | // 小提示: 切片和数组不一样,它是引用。如果是数组的话,那下面的 `assert!` 将会通过: '中'和'国'是char类型,char类型是Unicode编码,大小固定为4字节,两个字符为8字节。 30 | assert!(std::mem::size_of_val(&slice) == 8); 31 | } 32 | ``` 33 | 34 | 3. 🌟🌟 35 | ```rust,editable 36 | 37 | fn main() { 38 | let arr: [i32; 5] = [1, 2, 3, 4, 5]; 39 | // 填空让代码工作起来 40 | let slice: __ = __; 41 | assert_eq!(slice, &[2, 3, 4]); 42 | } 43 | ``` 44 | 45 | ### 字符串切片 46 | 4. 🌟 47 | ```rust,editable 48 | 49 | fn main() { 50 | let s = String::from("hello"); 51 | 52 | let slice1 = &s[0..2]; 53 | // 填空,不要再使用 0..2 54 | let slice2 = &s[__]; 55 | 56 | assert_eq!(slice1, slice2); 57 | } 58 | ``` 59 | 60 | 5. 🌟 61 | ```rust,editable 62 | 63 | fn main() { 64 | let s = "你好,世界"; 65 | // 修改以下代码行,让代码工作起来 66 | let slice = &s[0..2]; 67 | 68 | assert!(slice == "你"); 69 | } 70 | ``` 71 | 72 | 6. 🌟🌟 `&String` 可以被隐式地转换成 `&str` 类型. 73 | ```rust,editable 74 | 75 | // 修复所有错误 76 | fn main() { 77 | let mut s = String::from("hello world"); 78 | 79 | // 这里, &s 是 `&String` 类型,但是 `first_character` 函数需要的是 `&str` 类型。 80 | // 尽管两个类型不一样,但是代码仍然可以工作,原因是 `&String` 会被隐式地转换成 `&str` 类型,如果大家想要知道更多,可以看看 Deref 章节: https://course.rs/advance/smart-pointer/deref.html 81 | let ch = first_character(&s); 82 | 83 | s.clear(); // error! 84 | 85 | println!("the first character is: {}", ch); 86 | } 87 | fn first_character(s: &str) -> &str { 88 | &s[..1] 89 | } 90 | ``` 91 | 92 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/compound-types/slice.md)找到答案(在 solutions 路径下) 93 | -------------------------------------------------------------------------------- /zh-CN/src/compound-types/tuple.md: -------------------------------------------------------------------------------- 1 | # 元组( Tuple ) 2 | 1. 🌟 元组中的元素可以是不同的类型。元组的类型签名是 `(T1, T2, ...)`, 这里 `T1`, `T2` 是相对应的元组成员的类型. 3 | ```rust,editable 4 | 5 | fn main() { 6 | let _t0: (u8,i16) = (0, -1); 7 | // 元组的成员还可以是一个元组 8 | let _t1: (u8, (i16, u32)) = (0, (-1, 1)); 9 | // 填空让代码工作 10 | let t: (u8, __, i64, __, __) = (1u8, 2u16, 3i64, "hello", String::from(", world")); 11 | } 12 | ``` 13 | 14 | 2. 🌟 可以使用索引来获取元组的成员 15 | ```rust,editable 16 | 17 | // 修改合适的地方,让代码工作 18 | fn main() { 19 | let t = ("i", "am", "sunface"); 20 | assert_eq!(t.1, "sunface"); 21 | } 22 | ``` 23 | 24 | 3. 🌟 过长的元组无法被打印输出 25 | ```rust,editable 26 | 27 | // 修复代码错误 28 | fn main() { 29 | let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); 30 | println!("too long tuple: {:?}", too_long_tuple); 31 | } 32 | ``` 33 | 34 | 4. 🌟 使用模式匹配来解构元组 35 | ```rust,editable 36 | 37 | fn main() { 38 | let tup = (1, 6.4, "hello"); 39 | 40 | // 填空 41 | let __ = tup; 42 | 43 | assert_eq!(x, 1); 44 | assert_eq!(y, "hello"); 45 | assert_eq!(z, 6.4); 46 | } 47 | ``` 48 | 49 | 5. 🌟🌟 解构式赋值 50 | ```rust,editable 51 | fn main() { 52 | let (x, y, z); 53 | 54 | // 填空 55 | __ = (1, 2, 3); 56 | 57 | assert_eq!(x, 3); 58 | assert_eq!(y, 1); 59 | assert_eq!(z, 2); 60 | } 61 | ``` 62 | 63 | 6. 🌟🌟 元组可以用于函数的参数和返回值 64 | ```rust,editable 65 | 66 | fn main() { 67 | // 填空,需要稍微计算下 68 | let (x, y) = sum_multiply(__); 69 | 70 | assert_eq!(x, 5); 71 | assert_eq!(y, 6); 72 | } 73 | 74 | fn sum_multiply(nums: (i32, i32)) -> (i32, i32) { 75 | (nums.0 + nums.1, nums.0 * nums.1) 76 | } 77 | ``` 78 | 79 | 80 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/compound-types/tuple.md)找到答案(在 solutions 路径下) -------------------------------------------------------------------------------- /zh-CN/src/crate-module/crate.md: -------------------------------------------------------------------------------- 1 | # Package and Crate 2 | `package` 是你通过 `Cargo` 创建的工程或项目,因此在 `package` 的根目录下会有一个 `Cargo.toml` 文件。 3 | 4 | 1. 🌟 创建一个 `package`,拥有以下目录结构: 5 | ```shell 6 | . 7 | ├── Cargo.toml 8 | └── src 9 | └── main.rs 10 | 11 | 1 directory, 2 files 12 | ``` 13 | 14 | ```toml 15 | # in Cargo.toml 16 | [package] 17 | name = "hello-package" 18 | version = "0.1.0" 19 | edition = "2021" 20 | ``` 21 | 22 | > 注意! 我们会在包与模块中使用上面的项目作为演示,因此不要删除 23 | 24 | 2. 🌟 创建一个 package,拥有以下目录结构: 25 | ```shell 26 | . 27 | ├── Cargo.toml 28 | └── src 29 | └── lib.rs 30 | 31 | 1 directory, 2 files 32 | ``` 33 | 34 | ```toml 35 | # in Cargo.toml 36 | [package] 37 | name = "hello-package1" 38 | version = "0.1.0" 39 | edition = "2021" 40 | ``` 41 | 42 | > 该项目可以安全的移除 43 | 44 | 3. 🌟 45 | ```rust,editable 46 | /* 使用你的答案填空 */ 47 | 48 | // Q: package 1# 和 2# 的区别是什么 ? 49 | // A: __ 50 | ``` 51 | 52 | 53 | ## 包Crate 54 | 一个包可以是二进制也可以一个依赖库。每一个包都有一个包根,例如二进制包的包根是 `src/main.rs`,库包的包根是 `src/lib.rs`。包根是编译器开始处理源代码文件的地方,同时也是包模块树的根部。 55 | 56 | 在 package `hello-package` 中,有一个二进制包,该包与 `package` 同名 : `hello-package`, 其中 `src/main.rs` 是该二进制包的包根. 57 | 58 | 与 `hello-package` 类似, `hello-package1` 同样包含一个包,但是与之前的二进制包不同,该 package 包含的是库包,其中 `src/lib.rs` 是其包根. 59 | 60 | 4. 🌟 61 | ```rust,editable 62 | /* 填空 */ 63 | 64 | // Q: package `hello-package1` 中的库包名称是? 65 | // A: __ 66 | ``` 67 | 68 | 69 | 5. 🌟🌟 为 `hello-package` 添加一个库包,并且完成以下目录结构的填空: 70 | ```shell,editable 71 | # 填空 72 | . 73 | ├── Cargo.lock 74 | ├── Cargo.toml 75 | ├── src 76 | │   ├── __ 77 | │   └── __ 78 | ``` 79 | 80 | 在上一个步骤后,我们的 `hello-package` 中已经存在两个包:一个二进制包和一个库包,两个包的名称都与 package 相同:`hello-package`。 81 | 82 | 83 | 84 | 6. 🌟🌟🌟 一个 package 最多只能包含一个库包,但是却可以包含多个二进制包:通过将二进制文件放入到 `src/bin` 目录下实现: **该目录下的每个文件都是一个独立的二进制包,包名与文件名相同,不再与 package 的名称相同。**. 85 | 86 | ```shell,editable 87 | # 创建一个 a package 包含以下包: 88 | # 1. 三个二进制包: `hello-package`, `main1` and `main2` 89 | # 2. 一个库包 90 | # 并完成以下目录结构的填空 91 | . 92 | ├── Cargo.toml 93 | ├── Cargo.lock 94 | ├── src 95 | │ ├── __ 96 | │ ├── __ 97 | │ └── __ 98 | │ └── __ 99 | │ └── __ 100 | ├── tests # 存放集成测试文件的目录 101 | │ └── some_integration_tests.rs 102 | ├── benches # 存放 benchmark 文件的目录dir for benchmark files 103 | │ └── simple_bench.rs 104 | └── examples # 存放示例文件的目录 105 | └── simple_example.rs 106 | ``` 107 | 108 | 可以看到,上面的 package 结构非常标准,你可以在很多 Rust 项目中看到该结构的身影。 109 | 110 | 111 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/crate-module/crate.md)找到答案(在 solutions 路径下) -------------------------------------------------------------------------------- /zh-CN/src/crate-module/intro.md: -------------------------------------------------------------------------------- 1 | # 包和模块 2 | 3 | 学习资料: 4 | 5 | - 简体中文: [Rust语言圣经 - 包和模块](https://course.rs/basic/crate-module/intro.html) 6 | 7 | -------------------------------------------------------------------------------- /zh-CN/src/crate-module/use-pub.md: -------------------------------------------------------------------------------- 1 | # use and pub 2 | 1. 🌟 使用 `use` 可以将两个同名类型引入到当前作用域中,但是别忘了 `as` 关键字. 3 | 4 | ```rust,editable 5 | use std::fmt::Result; 6 | use std::io::Result; 7 | 8 | fn main() {} 9 | ``` 10 | 11 | 2. 🌟🌟 如果我们在使用来自同一个包或模块中的多个不同项,那么可以通过简单的方式将它们一次性引入进来 12 | 13 | ```rust,editable 14 | 15 | // 使用两种方式填空 16 | // 不要添加新的代码行 17 | use std::collections::__; 18 | 19 | fn main() { 20 | let _c1:HashMap<&str, i32> = HashMap::new(); 21 | let mut c2 = BTreeMap::new(); 22 | c2.insert(1, "a"); 23 | let _c3: HashSet = HashSet::new(); 24 | } 25 | ``` 26 | 27 | ### 使用 `pub use` 进行再导出 28 | 29 | 3. 🌟🌟🌟 在之前创建的`hello-package` 的库包中, 添加一些代码让下面的代码能够正常工作 30 | ```rust,editable 31 | fn main() { 32 | assert_eq!(hello_package::hosting::seat_at_table(), "sit down please"); 33 | assert_eq!(hello_package::eat_at_restaurant(),"yummy yummy!"); 34 | } 35 | ``` 36 | 37 | 38 | ### pub(in Crate) 39 | 有时我们希望某一个项只对特定的包可见,那么就可以使用 `pub(in Crate)` 语法. 40 | 41 | #### 示例 42 | ```rust,editable 43 | pub mod a { 44 | pub const I: i32 = 3; 45 | 46 | fn semisecret(x: i32) -> i32 { 47 | use self::b::c::J; 48 | x + J 49 | } 50 | 51 | pub fn bar(z: i32) -> i32 { 52 | semisecret(I) * z 53 | } 54 | pub fn foo(y: i32) -> i32 { 55 | semisecret(I) + y 56 | } 57 | 58 | mod b { 59 | pub(in crate::a) mod c { 60 | pub(in crate::a) const J: i32 = 4; 61 | } 62 | } 63 | } 64 | ``` 65 | 66 | ### 完整代码 67 | 至此,包与模块章节已经结束,关于 `hello-package` 的完整代码可以在[这里](https://github.com/sunface/rust-by-practice/tree/master/practices/hello-package) 找到. 68 | 69 | 70 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/crate-module/use-pub.md)找到答案(在 solutions 路径下) -------------------------------------------------------------------------------- /zh-CN/src/elegant-code-base.md: -------------------------------------------------------------------------------- 1 | # 值得学习的小型项目 2 | 在国内外的各大 Rust 论坛上,以下问题非常常见: 3 | 4 | - 作为 Rust 新手,有哪些项目值得推荐学习? 5 | - 求推荐代码优雅的小型项目 6 | - 有哪些值得推荐的简单、易读的项目 7 | 8 | 这些问题的答案往往只有一个,那就是实践:做一些优秀的练习题,然后阅读一些小而美的 Rust 项目。 9 | 10 | 这个恰恰跟本书的目标吻合,因此,我们决定收集一些优秀的资源,并在 _Rust语言实战_ 中呈现给大家。 11 | 12 | ### 1. Ripgrep 13 | 14 | 以上的问题通常都会伴随着 [`ripgrep`](https://github.com/BurntSushi/ripgrep) 的推荐, 虽然我不认为它是一个小型项目,但是依然非常推荐大家学习,当然,首先你得做好深挖的准备和耐心。 15 | 16 | ### 2. 教程:构建一个文本编辑器 17 | 18 | 该教程 [`https://www.flenker.blog/hecto/`](https://www.flenker.blog/hecto/) 将带领我们从零开始构建一个文本编辑器. 19 | 20 | ### 3. Ncspot 21 | 22 | [Ncspot](https://github.com/hrkfdn/ncspot) 是一个终端访问的 Spotify 客户端,小巧、简单、良好的代码组织以及异步编程,值得学习. 23 | 24 | ### 4. 命令行 Rust 25 | 26 | [这个项目](https://github.com/kyclark/command-line-rust) 是书本 `Command-Line Rust(O'Reily)` 的配套项目,可以帮助大家理解该如何更好的编写命令行程序,例如 `head`, `cat`, `ls`。 27 | 28 | ### 5. 在 PNG 中隐藏你的秘密 29 | 30 | [这本书](https://jrdngr.github.io/pngme_book/) 将带领大家编写一个命令行程序,功能是在 PNG 文件中隐藏一些秘密信息,首要目标是让我们熟悉 Rust 代码。 31 | 32 | ### 6. 使用 Rust 写一个小型 OS 33 | 34 | [这个博客系列](https://os.phil-opp.com) 会带领大家使用 Rust 语言创建一个小型的操作系统。其中每一篇文章都是一个小的教程并包含完整的代码。 35 | 36 | 你也可以在[以下地址](https://github.com/phil-opp/blog_os)找到完整的源代码。 37 | 38 | ### 7. CodeCrafters.io: 写一个你自己的 Git, Docker,SQLite 或 Redis 39 | 40 | 在 [CodeCrafters](https://codecrafters.io/for/rust) 上,你可以从头开始重新创建您最喜欢的开发人员工具。这是一种掌握 Rust 的实践、最低限度指导的方法,同时欣赏我们每天使用的流行技术的内部结构和文档。 41 | 42 | ### 8. mini-redis 43 | [mini-redis](https://github.com/tokio-rs/mini-redis) 是一个不完整的 Redis 客户端、服务器实现,由 tokio 官方出品,代码质量非常高,而且有详细的注释,非常适合学习 Rust 和异步编程。 44 | 45 | ### 9. 使用 Rust 写一个解析器 46 | 47 | [这本开源书](https://rust-hosted-langs.github.io/book/) 是一个使用 Rust 语言实现编译型语言的教程。 48 | 49 | ### 10. Rust编写推箱子游戏教程 50 | [sokoban](https://sokoban.iolivia.me/zh_cn/c01-00-intro) 是一个使用 Rust 语言实现 sokoban 游戏的教程。虽然社区已停止活跃( Initial Commits on Sep 3, 2019,GitHub 仓库最后一条 commit 是 last year ),但是项目是完善的 release v0.1.0,拿来练手是个不错的选择。 51 | 52 | **To be continued...** 53 | -------------------------------------------------------------------------------- /zh-CN/src/errors.md: -------------------------------------------------------------------------------- 1 | # Errors 2 | -------------------------------------------------------------------------------- /zh-CN/src/functional-programing/closure.md: -------------------------------------------------------------------------------- 1 | # Closure 2 | 3 | 下面代码是Rust圣经课程中[闭包](http://course.rs/advance/functional-programing/closure.html#结构体中的闭包)章节的课内练习题答案: 4 | 5 | ```rust 6 | struct Cacher 7 | where 8 | T: Fn(E) -> E, 9 | E: Copy 10 | { 11 | query: T, 12 | value: Option, 13 | } 14 | 15 | impl Cacher 16 | where 17 | T: Fn(E) -> E, 18 | E: Copy 19 | { 20 | fn new(query: T) -> Cacher { 21 | Cacher { 22 | query, 23 | value: None, 24 | } 25 | } 26 | 27 | fn value(&mut self, arg: E) -> E { 28 | match self.value { 29 | Some(v) => v, 30 | None => { 31 | let v = (self.query)(arg); 32 | self.value = Some(v); 33 | v 34 | } 35 | } 36 | } 37 | } 38 | fn main() { 39 | 40 | } 41 | 42 | #[test] 43 | fn call_with_different_values() { 44 | let mut c = Cacher::new(|a| a); 45 | 46 | let v1 = c.value(1); 47 | let v2 = c.value(2); 48 | 49 | assert_eq!(v2, 1); 50 | } 51 | ``` 52 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/functional-programing/closure.md)找到答案(在 solutions 路径下) 53 | -------------------------------------------------------------------------------- /zh-CN/src/functional-programing/intro.md: -------------------------------------------------------------------------------- 1 | # Functional programing 2 | -------------------------------------------------------------------------------- /zh-CN/src/functional-programing/iterator.md: -------------------------------------------------------------------------------- 1 | # Iterator 2 | 3 | 4 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/functional-programing/iterator.md)找到答案(在 solutions 路径下) 5 | -------------------------------------------------------------------------------- /zh-CN/src/functional-programming/closure.md: -------------------------------------------------------------------------------- 1 | # Closure 2 | 3 | 下面代码是Rust圣经课程中[闭包](http://course.rs/advance/functional-programing/closure.html#结构体中的闭包)章节的课内练习题答案: 4 | 5 | ```rust 6 | struct Cacher 7 | where 8 | T: Fn(E) -> E, 9 | E: Copy 10 | { 11 | query: T, 12 | value: Option, 13 | } 14 | 15 | impl Cacher 16 | where 17 | T: Fn(E) -> E, 18 | E: Copy 19 | { 20 | fn new(query: T) -> Cacher { 21 | Cacher { 22 | query, 23 | value: None, 24 | } 25 | } 26 | 27 | fn value(&mut self, arg: E) -> E { 28 | match self.value { 29 | Some(v) => v, 30 | None => { 31 | let v = (self.query)(arg); 32 | self.value = Some(v); 33 | v 34 | } 35 | } 36 | } 37 | } 38 | fn main() { 39 | 40 | } 41 | 42 | #[test] 43 | fn call_with_different_values() { 44 | let mut c = Cacher::new(|a| a); 45 | 46 | let v1 = c.value(1); 47 | let v2 = c.value(2); 48 | 49 | assert_eq!(v2, 1); 50 | } 51 | ``` -------------------------------------------------------------------------------- /zh-CN/src/functional-programming/intro.md: -------------------------------------------------------------------------------- 1 | # Functional Programming 2 | -------------------------------------------------------------------------------- /zh-CN/src/generics-traits/const-generics.md: -------------------------------------------------------------------------------- 1 | # Const 泛型 2 | 在之前的泛型中,可以抽象为一句话:针对类型实现的泛型,所有的泛型都是为了抽象不同的类型,那有没有针对值的泛型?答案就是 `Const 泛型`。 3 | 4 | ## 示例 5 | 1. 下面的例子同时使用泛型和 const 泛型来实现一个结构体,该结构体的字段中的数组长度是可变的 6 | ```rust,editable 7 | struct ArrayPair { 8 | left: [T; N], 9 | right: [T; N], 10 | } 11 | 12 | impl Debug for ArrayPair { 13 | // ... 14 | } 15 | ``` 16 | 17 | 18 | 2. 目前,const 泛型参数只能使用以下形式的实参: 19 | 20 | - 一个单独的 const 泛型参数 21 | - 一个字面量 (i.e. 整数, 布尔值或字符). 22 | - 一个具体的 const 表达式( 表达式中不能包含任何 泛型参数) 23 | 24 | ```rust,editable 25 | fn foo() {} 26 | 27 | fn bar() { 28 | foo::(); // ok: 符合第一种 29 | foo::<2021>(); // ok: 符合第二种 30 | foo::<{20 * 100 + 20 * 10 + 1}>(); // ok: 符合第三种 31 | 32 | foo::<{ M + 1 }>(); // error: 违背第三种,const 表达式中不能有泛型参数 M 33 | foo::<{ std::mem::size_of::() }>(); // error: 泛型表达式包含了泛型参数 T 34 | 35 | let _: [u8; M]; // ok: 符合第一种 36 | let _: [u8; std::mem::size_of::()]; // error: 泛型表达式包含了泛型参数 T 37 | } 38 | 39 | fn main() {} 40 | ``` 41 | 42 | 3. const 泛型还能帮我们避免一些运行时检查,提升性能 43 | ```rust 44 | pub struct MinSlice { 45 | pub head: [T; N], 46 | pub tail: [T], 47 | } 48 | 49 | fn main() { 50 | let slice: &[u8] = b"Hello, world"; 51 | let reference: Option<&u8> = slice.get(6); 52 | // 我们知道 `.get` 返回的是 `Some(b' ')` 53 | // 但编译器不知道 54 | assert!(reference.is_some()); 55 | 56 | let slice: &[u8] = b"Hello, world"; 57 | 58 | // 当编译构建 MinSlice 时会进行长度检查,也就是在编译期我们就知道它的长度是 12 59 | // 在运行期,一旦 `unwrap` 成功,在 `MinSlice` 的作用域内,就再无需任何检查 60 | let minslice = MinSlice::::from_slice(slice).unwrap(); 61 | let value: u8 = minslice.head[6]; 62 | assert_eq!(value, b' ') 63 | } 64 | ``` 65 | 66 | 67 | ## 练习 68 | 1. 🌟🌟 `` 是结构体类型的一部分,和数组类型一样,这意味着长度不同会导致类型不同: `Array` 和 `Array` 是不同的类型 69 | 70 | ```rust,editable 71 | 72 | // 修复错误 73 | struct Array { 74 | data : [T; N] 75 | } 76 | 77 | fn main() { 78 | let arrays = [ 79 | Array{ 80 | data: [1, 2, 3], 81 | }, 82 | Array { 83 | data: [1.0, 2.0, 3.0], 84 | }, 85 | Array { 86 | data: [1, 2] 87 | } 88 | ]; 89 | } 90 | ``` 91 | 92 | 2. 🌟🌟 93 | ```rust,editable 94 | 95 | // 填空 96 | fn print_array<__>(__) { 97 | println!("{:?}", arr); 98 | } 99 | fn main() { 100 | let arr = [1, 2, 3]; 101 | print_array(arr); 102 | 103 | let arr = ["hello", "world"]; 104 | print_array(arr); 105 | } 106 | ``` 107 | 108 | 3. 🌟🌟🌟 有时我们希望能限制一个变量占用内存的大小,例如在嵌入式环境中,此时 const 泛型参数的第三种形式 `const 表达式` 就非常适合. 109 | 110 | ```rust,editable 111 | #![allow(incomplete_features)] 112 | #![feature(generic_const_exprs)] 113 | 114 | fn check_size(val: T) 115 | where 116 | Assert<{ core::mem::size_of::() < 768 }>: IsTrue, 117 | { 118 | //... 119 | } 120 | 121 | // 修复 main 函数中的错误 122 | fn main() { 123 | check_size([0u8; 767]); 124 | check_size([0i32; 191]); 125 | check_size(["hello你好"; __]); // size of &str ? 126 | check_size([(); __].map(|_| "hello你好".to_string())); // size of String? 127 | check_size(['中'; __]); // size of char ? 128 | } 129 | 130 | 131 | 132 | pub enum Assert {} 133 | 134 | pub trait IsTrue {} 135 | 136 | impl IsTrue for Assert {} 137 | ``` 138 | 139 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/generics-traits/const-generics.md)找到答案(在 solutions 路径下) -------------------------------------------------------------------------------- /zh-CN/src/generics-traits/generics.md: -------------------------------------------------------------------------------- 1 | # 泛型 2 | 3 | ### 函数 4 | 1. 🌟🌟🌟 5 | ```rust,editable 6 | 7 | // 填空 8 | struct A; // 具体的类型 `A`. 9 | struct S(A); // 具体的类型 `S`. 10 | struct SGen(T); // 泛型 `SGen`. 11 | 12 | fn reg_fn(_s: S) {} 13 | 14 | fn gen_spec_t(_s: SGen) {} 15 | 16 | fn gen_spec_i32(_s: SGen) {} 17 | 18 | fn generic(_s: SGen) {} 19 | 20 | fn main() { 21 | // 使用非泛型函数 22 | reg_fn(__); // 具体的类型 23 | gen_spec_t(__); // 隐式地指定类型参数 `A`. 24 | gen_spec_i32(__); // 隐式地指定类型参数`i32`. 25 | 26 | // 显式地指定类型参数 `char` 27 | generic::(__); 28 | 29 | // 隐式地指定类型参数 `char`. 30 | generic(__); 31 | } 32 | ``` 33 | 34 | 2. 🌟🌟 35 | ```rust,editable 36 | 37 | // 实现下面的泛型函数 sum 38 | fn sum 39 | 40 | fn main() { 41 | assert_eq!(5, sum(2i8, 3i8)); 42 | assert_eq!(50, sum(20, 30)); 43 | assert_eq!(2.46, sum(1.23, 1.23)); 44 | } 45 | ``` 46 | 47 | 48 | ### 结构体和 `impl` 49 | 50 | 3. 🌟 51 | ```rust,editable 52 | 53 | // 实现一个结构体 Point 让代码工作 54 | 55 | 56 | fn main() { 57 | let integer = Point { x: 5, y: 10 }; 58 | let float = Point { x: 1.0, y: 4.0 }; 59 | } 60 | ``` 61 | 62 | 4. 🌟🌟 63 | ```rust,editable 64 | 65 | // 修改以下结构体让代码工作 66 | struct Point { 67 | x: T, 68 | y: T, 69 | } 70 | 71 | fn main() { 72 | // 不要修改这行代码! 73 | let p = Point{x: 5, y : "hello".to_string()}; 74 | } 75 | ``` 76 | 77 | 5. 🌟🌟 78 | ```rust,editable 79 | 80 | // 为 Val 增加泛型参数,不要修改 `main` 中的代码 81 | struct Val { 82 | val: f64, 83 | } 84 | 85 | impl Val { 86 | fn value(&self) -> &f64 { 87 | &self.val 88 | } 89 | } 90 | 91 | 92 | fn main() { 93 | let x = Val{ val: 3.0 }; 94 | let y = Val{ val: "hello".to_string()}; 95 | println!("{}, {}", x.value(), y.value()); 96 | } 97 | ``` 98 | 99 | ### 方法 100 | 6. 🌟🌟🌟 101 | 102 | ```rust,editable 103 | struct Point { 104 | x: T, 105 | y: U, 106 | } 107 | 108 | impl Point { 109 | // 实现 mixup,不要修改其它代码! 110 | fn mixup 111 | } 112 | 113 | fn main() { 114 | let p1 = Point { x: 5, y: 10 }; 115 | let p2 = Point { x: "Hello", y: '中'}; 116 | 117 | let p3 = p1.mixup(p2); 118 | 119 | assert_eq!(p3.x, 5); 120 | assert_eq!(p3.y, '中'); 121 | } 122 | ``` 123 | 124 | 7. 🌟🌟 125 | ```rust,editable 126 | 127 | // 修复错误,让代码工作 128 | struct Point { 129 | x: T, 130 | y: T, 131 | } 132 | 133 | impl Point { 134 | fn distance_from_origin(&self) -> f32 { 135 | (self.x.powi(2) + self.y.powi(2)).sqrt() 136 | } 137 | } 138 | 139 | fn main() { 140 | let p = Point{x: 5, y: 10}; 141 | println!("{}",p.distance_from_origin()) 142 | } 143 | ``` 144 | 145 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/generics-traits/generics.md)找到答案(在 solutions 路径下) 146 | 147 | -------------------------------------------------------------------------------- /zh-CN/src/generics-traits/intro.md: -------------------------------------------------------------------------------- 1 | # Generics and Traits 2 | -------------------------------------------------------------------------------- /zh-CN/src/global-variables.md: -------------------------------------------------------------------------------- 1 | # Global variables 2 | -------------------------------------------------------------------------------- /zh-CN/src/lifetime/intro.md: -------------------------------------------------------------------------------- 1 | # 生命周期 2 | 学习资料: 3 | - 简体中文: [Rust语言圣经 - 生命周期](https://course.rs/advance/lifetime/intro.html) 4 | 5 | -------------------------------------------------------------------------------- /zh-CN/src/macro.md: -------------------------------------------------------------------------------- 1 | # macro 2 | -------------------------------------------------------------------------------- /zh-CN/src/newtype-sized.md: -------------------------------------------------------------------------------- 1 | # newtype and Sized 2 | -------------------------------------------------------------------------------- /zh-CN/src/ownership/borrowing.md: -------------------------------------------------------------------------------- 1 | # 引用和借用 2 | 3 | ### 引用 4 | 1. 🌟 5 | ```rust,editable 6 | 7 | fn main() { 8 | let x = 5; 9 | // 填写空白处 10 | let p = __; 11 | 12 | println!("x 的内存地址是 {:p}", p); // output: 0x16fa3ac84 13 | } 14 | ``` 15 | 16 | 2. 🌟 17 | ```rust,editable 18 | 19 | fn main() { 20 | let x = 5; 21 | let y = &x; 22 | 23 | // 只能修改以下行 24 | assert_eq!(5, y); 25 | } 26 | ``` 27 | 28 | 3. 🌟 29 | ```rust,editable 30 | 31 | // 修复错误 32 | fn main() { 33 | let mut s = String::from("hello, "); 34 | 35 | borrow_object(s) 36 | } 37 | 38 | fn borrow_object(s: &String) {} 39 | ``` 40 | 41 | 4. 🌟 42 | ```rust,editable 43 | 44 | // 修复错误 45 | fn main() { 46 | let mut s = String::from("hello, "); 47 | 48 | push_str(s) 49 | } 50 | 51 | fn push_str(s: &mut String) { 52 | s.push_str("world") 53 | } 54 | ``` 55 | 56 | 5. 🌟🌟 57 | ```rust,editable 58 | 59 | fn main() { 60 | let mut s = String::from("hello, "); 61 | 62 | // 填写空白处,让代码工作 63 | let p = __; 64 | 65 | p.push_str("world"); 66 | } 67 | ``` 68 | 69 | #### ref 70 | `ref` 与 `&` 类似,可以用来获取一个值的引用,但是它们的用法有所不同。 71 | 72 | 6. 🌟🌟🌟 73 | ```rust,editable 74 | 75 | fn main() { 76 | let c = '中'; 77 | 78 | let r1 = &c; 79 | // 填写空白处,但是不要修改其它行的代码 80 | let __ r2 = c; 81 | 82 | assert_eq!(*r1, *r2); 83 | 84 | // 判断两个内存地址的字符串是否相等 85 | assert_eq!(get_addr(r1),get_addr(r2)); 86 | } 87 | 88 | // 获取传入引用的内存地址的字符串形式 89 | fn get_addr(r: &char) -> String { 90 | format!("{:p}", r) 91 | } 92 | ``` 93 | 94 | ### 借用规则 95 | 7. 🌟 96 | ```rust,editable 97 | 98 | // 移除代码某个部分,让它工作 99 | // 你不能移除整行的代码! 100 | fn main() { 101 | let mut s = String::from("hello"); 102 | 103 | let r1 = &mut s; 104 | let r2 = &mut s; 105 | 106 | println!("{}, {}", r1, r2); 107 | } 108 | ``` 109 | 110 | #### 可变性 111 | 8. 🌟 错误: 从不可变对象借用可变 112 | ```rust,editable 113 | 114 | fn main() { 115 | // 通过修改下面一行代码来修复错误 116 | let s = String::from("hello, "); 117 | 118 | borrow_object(&mut s) 119 | } 120 | 121 | fn borrow_object(s: &mut String) {} 122 | ``` 123 | 124 | 9. 🌟🌟 Ok: 从可变对象借用不可变 125 | ```rust,editable 126 | 127 | // 下面的代码没有任何错误 128 | fn main() { 129 | let mut s = String::from("hello, "); 130 | 131 | borrow_object(&s); 132 | 133 | s.push_str("world"); 134 | } 135 | 136 | fn borrow_object(s: &String) {} 137 | ``` 138 | 139 | ### NLL 140 | 10. 🌟🌟 141 | ```rust,editable 142 | 143 | // 注释掉一行代码让它工作 144 | fn main() { 145 | let mut s = String::from("hello, "); 146 | 147 | let r1 = &mut s; 148 | r1.push_str("world"); 149 | let r2 = &mut s; 150 | r2.push_str("!"); 151 | 152 | println!("{}",r1); 153 | } 154 | ``` 155 | 156 | 11. 🌟🌟 157 | ```rust,editable 158 | 159 | fn main() { 160 | let mut s = String::from("hello, "); 161 | 162 | let r1 = &mut s; 163 | let r2 = &mut s; 164 | 165 | // 在下面增加一行代码人为制造编译错误:cannot borrow `s` as mutable more than once at a time 166 | // 你不能同时使用 r1 和 r2 167 | } 168 | ``` 169 | 170 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/ownership/borrowing.md)找到答案(在 solutions 路径下) 171 | -------------------------------------------------------------------------------- /zh-CN/src/ownership/intro.md: -------------------------------------------------------------------------------- 1 | # 所有权与借用 2 | 学习资料 : 3 | - English: [Rust Book 4.1-4.4](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html) 4 | - 简体中文: [Rust语言圣经 - 所有权与借用](https://course.rs/basic/ownership/index.html) 5 | 6 | -------------------------------------------------------------------------------- /zh-CN/src/ownership/ownership.md: -------------------------------------------------------------------------------- 1 | # 所有权 2 | 3 | 1. 🌟🌟 4 | ```rust,editable 5 | 6 | fn main() { 7 | // 使用尽可能多的方法来通过编译 8 | let x = String::from("hello, world"); 9 | let y = x; 10 | println!("{},{}",x,y); 11 | } 12 | ``` 13 | 14 | 2. 🌟🌟 15 | ```rust,editable 16 | // 不要修改 main 中的代码 17 | fn main() { 18 | let s1 = String::from("hello, world"); 19 | let s2 = take_ownership(s1); 20 | 21 | println!("{}", s2); 22 | } 23 | 24 | // 只能修改下面的代码! 25 | fn take_ownership(s: String) { 26 | println!("{}", s); 27 | } 28 | ``` 29 | 30 | 31 | 3. 🌟🌟 32 | ```rust,editable 33 | 34 | fn main() { 35 | let s = give_ownership(); 36 | println!("{}", s); 37 | } 38 | 39 | // 只能修改下面的代码! 40 | fn give_ownership() -> String { 41 | let s = String::from("hello, world"); 42 | // convert String to Vec 43 | // 将 String 转换成 Vec 类型 44 | let _s = s.into_bytes(); 45 | s 46 | } 47 | ``` 48 | 49 | 4. 🌟🌟 50 | ```rust,editable 51 | // 修复错误,不要删除任何代码行 52 | fn main() { 53 | let s = String::from("hello, world"); 54 | 55 | print_str(s); 56 | 57 | println!("{}", s); 58 | } 59 | 60 | fn print_str(s: String) { 61 | println!("{}",s) 62 | } 63 | ``` 64 | 65 | 5. 🌟🌟 66 | ```rust,editable 67 | // 不要使用 clone,使用 copy 的方式替代 68 | fn main() { 69 | let x = (1, 2, (), "hello".to_string()); 70 | let y = x.clone(); 71 | println!("{:?}, {:?}", x, y); 72 | } 73 | ``` 74 | 75 | #### 可变性 76 | 当所有权转移时,可变性也可以随之改变。 77 | 78 | 6. 🌟 79 | ```rust,editable 80 | 81 | fn main() { 82 | let s = String::from("hello, "); 83 | 84 | // 只修改下面这行代码 ! 85 | let s1 = s; 86 | 87 | s1.push_str("world") 88 | } 89 | ``` 90 | 91 | 7. 🌟🌟🌟 92 | ```rust,editable 93 | 94 | fn main() { 95 | let x = Box::new(5); 96 | 97 | let ... // 完成该行代码,不要修改其它行! 98 | 99 | *y = 4; 100 | 101 | assert_eq!(*x, 5); 102 | } 103 | ``` 104 | 105 | ### 部分 move 106 | 当解构一个变量时,可以同时使用 `move` 和引用模式绑定的方式。当这么做时,部分 `move` 就会发生:变量中一部分的所有权被转移给其它变量,而另一部分我们获取了它的引用。 107 | 108 | 在这种情况下,原变量将无法再被使用,但是它没有转移所有权的那一部分依然可以使用,也就是之前被引用的那部分。 109 | 110 | #### 示例 111 | ```rust,editable 112 | 113 | fn main() { 114 | #[derive(Debug)] 115 | struct Person { 116 | name: String, 117 | age: Box, 118 | } 119 | 120 | let person = Person { 121 | name: String::from("Alice"), 122 | age: Box::new(20), 123 | }; 124 | 125 | // 通过这种解构式模式匹配,person.name 的所有权被转移给新的变量 `name` 126 | // 但是,这里 `age` 变量却是对 person.age 的引用, 这里 ref 的使用相当于: let age = &person.age 127 | let Person { name, ref age } = person; 128 | 129 | println!("The person's age is {}", age); 130 | 131 | println!("The person's name is {}", name); 132 | 133 | // Error! 原因是 person 的一部分已经被转移了所有权,因此我们无法再使用它 134 | //println!("The person struct is {:?}", person); 135 | 136 | // 虽然 `person` 作为一个整体无法再被使用,但是 `person.age` 依然可以使用 137 | println!("The person's age from person struct is {}", person.age); 138 | } 139 | ``` 140 | 141 | #### 练习 142 | 143 | 8. 🌟 144 | ```rust,editable 145 | 146 | fn main() { 147 | let t = (String::from("hello"), String::from("world")); 148 | 149 | let _s = t.0; 150 | 151 | // 仅修改下面这行代码,且不要使用 `_s` 152 | println!("{:?}", t); 153 | } 154 | ``` 155 | 156 | 9. 🌟🌟 157 | ```rust,editable 158 | 159 | fn main() { 160 | let t = (String::from("hello"), String::from("world")); 161 | 162 | // 填空,不要修改其它代码 163 | let (__, __) = __; 164 | 165 | println!("{:?}, {:?}, {:?}", s1, s2, t); // -> "hello", "world", ("hello", "world") 166 | } 167 | ``` 168 | 169 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/ownership/ownership.md)找到答案(在 solutions 路径下) 170 | -------------------------------------------------------------------------------- /zh-CN/src/pattern-match/intro.md: -------------------------------------------------------------------------------- 1 | # Pattern Match 2 | -------------------------------------------------------------------------------- /zh-CN/src/pattern-match/patterns.md: -------------------------------------------------------------------------------- 1 | # 模式 2 | 3 | 1. 🌟🌟 使用 `|` 可以匹配多个值, 而使用 `..=` 可以匹配一个闭区间的数值序列 4 | ```rust,editable 5 | 6 | fn main() {} 7 | fn match_number(n: i32) { 8 | match n { 9 | // 匹配一个单独的值 10 | 1 => println!("One!"), 11 | // 使用 `|` 填空,不要使用 `..` 或 `..=` 12 | __ => println!("match 2 -> 5"), 13 | // 匹配一个闭区间的数值序列 14 | 6..=10 => { 15 | println!("match 6 -> 10") 16 | }, 17 | _ => { 18 | println!("match 11 -> +infinite") 19 | } 20 | } 21 | } 22 | ``` 23 | 24 | 2. 🌟🌟🌟 `@` 操作符可以让我们将一个与模式相匹配的值绑定到新的变量上 25 | ```rust,editable 26 | 27 | struct Point { 28 | x: i32, 29 | y: i32, 30 | } 31 | 32 | fn main() { 33 | // 填空,让 p 匹配第二个分支 34 | let p = Point { x: __, y: __ }; 35 | 36 | match p { 37 | Point { x, y: 0 } => println!("On the x axis at {}", x), 38 | // 第二个分支 39 | Point { x: 0..=5, y: y@ (10 | 20 | 30) } => println!("On the y axis at {}", y), 40 | Point { x, y } => println!("On neither axis: ({}, {})", x, y), 41 | } 42 | } 43 | ``` 44 | 45 | 3. 🌟🌟🌟 46 | 47 | ```rust,editable 48 | 49 | // 修复错误 50 | enum Message { 51 | Hello { id: i32 }, 52 | } 53 | 54 | fn main() { 55 | let msg = Message::Hello { id: 5 }; 56 | 57 | match msg { 58 | Message::Hello { 59 | id: 3..=7, 60 | } => println!("id 值的范围在 [3, 7] 之间: {}", id), 61 | Message::Hello { id: newid@10 | 11 | 12 } => { 62 | println!("id 值的范围在 [10, 12] 之间: {}", newid) 63 | } 64 | Message::Hello { id } => println!("Found some other id: {}", id), 65 | } 66 | } 67 | ``` 68 | 69 | 4. 🌟🌟 匹配守卫(match guard)是一个位于 match 分支模式之后的额外 if 条件,它能为分支模式提供更进一步的匹配条件。 70 | ```rust,editable 71 | 72 | // 填空让代码工作,必须使用 `split` 73 | fn main() { 74 | let num = Some(4); 75 | let split = 5; 76 | match num { 77 | Some(x) __ => assert!(x < split), 78 | Some(x) => assert!(x >= split), 79 | None => (), 80 | } 81 | } 82 | ``` 83 | 84 | 5. 🌟🌟🌟 使用 `..` 忽略一部分值 85 | ```rust,editable 86 | 87 | // 填空,让代码工作 88 | fn main() { 89 | let numbers = (2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048); 90 | 91 | match numbers { 92 | __ => { 93 | assert_eq!(first, 2); 94 | assert_eq!(last, 2048); 95 | } 96 | } 97 | } 98 | ``` 99 | 100 | 6. 🌟🌟 使用模式 `&mut V` 去匹配一个可变引用时,你需要格外小心,因为匹配出来的 `V` 是一个值,而不是可变引用 101 | ```rust,editable 102 | 103 | // 修复错误,尽量少地修改代码 104 | // 不要移除任何代码行 105 | fn main() { 106 | let mut v = String::from("hello,"); 107 | let r = &mut v; 108 | 109 | match r { 110 | &mut value => value.push_str(" world!") 111 | } 112 | } 113 | ``` 114 | 115 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/pattern-match/patterns.md)找到答案(在 solutions 路径下) -------------------------------------------------------------------------------- /zh-CN/src/result-panic/intro.md: -------------------------------------------------------------------------------- 1 | # Result and panic 2 | Learning resources: 3 | - English: [Rust Book 9.1, 9.2](https://doc.rust-lang.org/book/ch09-00-error-handling.html) 4 | - 简体中文: [Rust语言圣经 - 返回值和错误处理](https://course.rs/basic/result-error/intro.html) 5 | 6 | -------------------------------------------------------------------------------- /zh-CN/src/result-panic/panic.md: -------------------------------------------------------------------------------- 1 | # panic! 2 | Rust 中最简单的错误处理方式就是使用 `panic`。它会打印出一条错误信息并打印出栈调用情况,最终结束当前线程: 3 | 4 | - 若 panic 发生在 `main` 线程,那程序会随之退出 5 | - 如果是在生成的( spawn )子线程中发生 panic, 那么当前的线程会结束,但是程序依然会继续运行 6 | 7 | 8 | 1. 🌟🌟 9 | ```rust,editable 10 | 11 | // 填空 12 | fn drink(beverage: &str) { 13 | if beverage == "lemonade" { 14 | println!("Success!"); 15 | // 实现下面的代码 16 | __ 17 | } 18 | 19 | println!("Exercise Failed if printing out this line!"); 20 | } 21 | 22 | fn main() { 23 | drink(__); 24 | 25 | println!("Exercise Failed if printing out this line!"); 26 | } 27 | ``` 28 | 29 | ## 常见的 panic 30 | 2. 🌟🌟 31 | ```rust,editable 32 | // 修复所有的 panic,让代码工作 33 | fn main() { 34 | assert_eq!("abc".as_bytes(), [96, 97, 98]); 35 | 36 | let v = vec![1, 2, 3]; 37 | let ele = v[3]; 38 | let ele = v.get(3).unwrap(); 39 | 40 | // 大部分时候编译器是可以帮我们提前发现溢出错误,并阻止编译通过。但是也有一些时候,这种溢出问题直到运行期才会出现 41 | let v = production_rate_per_hour(2); 42 | 43 | divide(15, 0); 44 | 45 | println!("Success!") 46 | } 47 | 48 | fn divide(x:u8, y:u8) { 49 | println!("{}", x / y) 50 | } 51 | 52 | fn production_rate_per_hour(speed: u8) -> f64 { 53 | let cph: u8 = 221; 54 | match speed { 55 | 1..=4 => (speed * cph) as f64, 56 | 5..=8 => (speed * cph) as f64 * 0.9, 57 | 9..=10 => (speed * cph) as f64 * 0.77, 58 | _ => 0 as f64, 59 | } 60 | } 61 | 62 | pub fn working_items_per_minute(speed: u8) -> u32 { 63 | (production_rate_per_hour(speed) / 60 as f64) as u32 64 | } 65 | ``` 66 | 67 | ### 详细的栈调用信息 68 | 默认情况下,栈调用只会展示最基本的信息: 69 | ```shell 70 | thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src/main.rs:4:5 71 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 72 | ``` 73 | 74 | 但是有时候,我们还希望获取更详细的信息: 75 | 76 | 3. 🌟 77 | ```shell 78 | ## 填空以打印全部的调用栈 79 | ## 提示: 你可以在之前的默认 panic 信息中找到相关线索 80 | $ __ cargo run 81 | thread 'main' panicked at 'assertion failed: `(left == right)` 82 | left: `[97, 98, 99]`, 83 | right: `[96, 97, 98]`', src/main.rs:3:5 84 | stack backtrace: 85 | 0: rust_begin_unwind 86 | at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:498:5 87 | 1: core::panicking::panic_fmt 88 | at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panicking.rs:116:14 89 | 2: core::panicking::assert_failed_inner 90 | 3: core::panicking::assert_failed 91 | at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panicking.rs:154:5 92 | 4: study_cargo::main 93 | at ./src/main.rs:3:5 94 | 5: core::ops::function::FnOnce::call_once 95 | at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/ops/function.rs:227:5 96 | note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. 97 | ``` 98 | 99 | ### `unwinding` 和 `abort` 100 | 101 | 当出现 `panic!` 时,程序提供了两种方式来处理终止流程:**栈展开**和**直接终止**。 102 | 103 | 其中,默认的方式就是 `栈展开`,这意味着 Rust 会回溯栈上数据和函数调用,因此也意味着更多的善后工作,好处是可以给出充分的报错信息和栈调用信息,便于事后的问题复盘。`直接终止`,顾名思义,不清理数据就直接退出程序,善后工作交与操作系统来负责。 104 | 105 | 对于绝大多数用户,使用默认选择是最好的,但是当你关心最终编译出的二进制可执行文件大小时,那么可以尝试去使用直接终止的方式,例如下面的配置修改 `Cargo.toml` 文件,实现在 [`release`](../first-try/cargo.md#手动编译和运行项目) 模式下遇到 `panic` 直接终止: 106 | 107 | ```rust 108 | [profile.release] 109 | panic = 'abort' 110 | ``` 111 | 112 | 113 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/result-panic/panic.md)找到答案(在 solutions 路径下) 114 | -------------------------------------------------------------------------------- /zh-CN/src/self-referential.md: -------------------------------------------------------------------------------- 1 | # Self referential 2 | -------------------------------------------------------------------------------- /zh-CN/src/smart-pointers/box.md: -------------------------------------------------------------------------------- 1 | # Box 2 | -------------------------------------------------------------------------------- /zh-CN/src/smart-pointers/cell-refcell.md: -------------------------------------------------------------------------------- 1 | # Cell and RefCell 2 | -------------------------------------------------------------------------------- /zh-CN/src/smart-pointers/deref.md: -------------------------------------------------------------------------------- 1 | # Deref 2 | -------------------------------------------------------------------------------- /zh-CN/src/smart-pointers/drop.md: -------------------------------------------------------------------------------- 1 | # Drop 2 | -------------------------------------------------------------------------------- /zh-CN/src/smart-pointers/intro.md: -------------------------------------------------------------------------------- 1 | # Smart pointers 2 | -------------------------------------------------------------------------------- /zh-CN/src/smart-pointers/rc-arc.md: -------------------------------------------------------------------------------- 1 | # Rc and Arc 2 | -------------------------------------------------------------------------------- /zh-CN/src/tests/assertions.md: -------------------------------------------------------------------------------- 1 | # Assertions 2 | -------------------------------------------------------------------------------- /zh-CN/src/tests/benchmark.md: -------------------------------------------------------------------------------- 1 | # Benchmark 2 | 3 | https://doc.rust-lang.org/unstable-book/library-features/test.html -------------------------------------------------------------------------------- /zh-CN/src/tests/intro.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | -------------------------------------------------------------------------------- /zh-CN/src/tests/unit-integration.md: -------------------------------------------------------------------------------- 1 | # Unit and Integration 2 | -------------------------------------------------------------------------------- /zh-CN/src/tests/write-tests.md: -------------------------------------------------------------------------------- 1 | # Write Tests 2 | -------------------------------------------------------------------------------- /zh-CN/src/threads/atomic.md: -------------------------------------------------------------------------------- 1 | # Atomic 2 | -------------------------------------------------------------------------------- /zh-CN/src/threads/basic-using.md: -------------------------------------------------------------------------------- 1 | # Basic using 2 | -------------------------------------------------------------------------------- /zh-CN/src/threads/intro.md: -------------------------------------------------------------------------------- 1 | # Threads 2 | -------------------------------------------------------------------------------- /zh-CN/src/threads/message-passing.md: -------------------------------------------------------------------------------- 1 | # Message passing 2 | -------------------------------------------------------------------------------- /zh-CN/src/threads/send-sync.md: -------------------------------------------------------------------------------- 1 | # Send and Sync 2 | -------------------------------------------------------------------------------- /zh-CN/src/threads/sync.md: -------------------------------------------------------------------------------- 1 | # Sync 2 | -------------------------------------------------------------------------------- /zh-CN/src/type-conversions/as.md: -------------------------------------------------------------------------------- 1 | # 使用 as 进行类型转换 2 | Rust 并没有为基本类型提供隐式的类型转换( coercion ),但是我们可以通过 `as` 来进行显式地转换。 3 | 4 | 1. 🌟 5 | ```rust,editable 6 | // 修复错误,填空 7 | // 不要移除任何代码 8 | fn main() { 9 | let decimal = 97.123_f32; 10 | 11 | let integer: __ = decimal as u8; 12 | 13 | let c1: char = decimal as char; 14 | let c2 = integer as char; 15 | 16 | assert_eq!(integer, 'b' as u8); 17 | 18 | println!("Success!") 19 | } 20 | ``` 21 | 22 | 2. 🌟🌟 默认情况下, 数值溢出会导致编译错误,但是我们可以通过添加一行全局注解的方式来避免编译错误(溢出还是会发生) 23 | ```rust,editable 24 | fn main() { 25 | assert_eq!(u8::MAX, 255); 26 | // 如上所示,u8 类型允许的最大值是 255. 27 | // 因此以下代码会报溢出的错误: literal out of range for `u8`. 28 | // **请仔细查看相应的编译错误,从中寻找到解决的办法** 29 | // **不要修改 main 中的任何代码** 30 | let v = 1000 as u8; 31 | 32 | println!("Success!") 33 | } 34 | ``` 35 | 36 | 3. 🌟🌟 当将任何数值转换成无符号整型 `T` 时,如果当前的数值不在新类型的范围内,我们可以对当前数值进行加值或减值操作( 增加或减少 `T::MAX + 1` ),直到最新的值在新类型的范围内,假设我们要将 `300` 转成 `u8` 类型,由于`u8` 最大值是 255,因此 `300` 不在新类型的范围内并且大于新类型的最大值,因此我们需要减去 `T::MAX + 1`,也就是 `300` - `256` = `44`。 37 | 38 | ```rust,editable 39 | fn main() { 40 | assert_eq!(1000 as u16, __); 41 | 42 | assert_eq!(1000 as u8, __); 43 | 44 | // 事实上,之前说的规则对于正整数而言,就是如下的取模 45 | println!("1000 mod 256 is : {}", 1000 % 256); 46 | 47 | assert_eq!(-1_i8 as u8, __); 48 | 49 | 50 | // 从 Rust 1.45 开始,当浮点数超出目标整数的范围时,转化会直接取正整数取值范围的最大或最小值 51 | assert_eq!(300.1_f32 as u8, __); 52 | assert_eq!(-100.1_f32 as u8, __); 53 | 54 | 55 | // 上面的浮点数转换有一点性能损耗,如果大家对于某段代码有极致的性能要求, 56 | // 可以考虑下面的方法,但是这些方法的结果可能会溢出并且返回一些无意义的值 57 | // 总之,请小心使用 58 | unsafe { 59 | // 300.0 is 44 60 | println!("300.0 is {}", 300.0_f32.to_int_unchecked::()); 61 | // -100.0 as u8 is 156 62 | println!("-100.0 as u8 is {}", (-100.0_f32).to_int_unchecked::()); 63 | // nan as u8 is 0 64 | println!("nan as u8 is {}", f32::NAN.to_int_unchecked::()); 65 | } 66 | } 67 | ``` 68 | 69 | 4. 🌟🌟🌟 裸指针可以和代表内存地址的整数互相转换 70 | ```rust,editable 71 | 72 | // 填空 73 | fn main() { 74 | let mut values: [i32; 2] = [1, 2]; 75 | let p1: *mut i32 = values.as_mut_ptr(); 76 | let first_address: usize = p1 __; 77 | let second_address = first_address + 4; // 4 == std::mem::size_of::() 78 | let p2: *mut i32 = second_address __; // p2 指向 values 数组中的第二个元素 79 | unsafe { 80 | // 将第二个元素加 1 81 | __ 82 | } 83 | 84 | assert_eq!(values[1], 3); 85 | 86 | println!("Success!") 87 | } 88 | ``` 89 | 90 | 91 | 5. 🌟🌟🌟 92 | ```rust,editable 93 | fn main() { 94 | let arr :[u64; 13] = [0; 13]; 95 | assert_eq!(std::mem::size_of_val(&arr), 8 * 13); 96 | let a: *const [u64] = &arr; 97 | let b = a as *const [u8]; 98 | unsafe { 99 | assert_eq!(std::mem::size_of_val(&*b), __) 100 | } 101 | } 102 | ``` 103 | 104 | > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/type-conversions/as.md)找到答案(在 solutions 路径下) 105 | -------------------------------------------------------------------------------- /zh-CN/src/type-conversions/intro.md: -------------------------------------------------------------------------------- 1 | # Type conversions 2 | There are several ways we can use to perform type conversions, such as `as`, `From/Intro`, `TryFrom/TryInto`, `transmute` etc. 3 | -------------------------------------------------------------------------------- /zh-CN/src/unsafe/intro.md: -------------------------------------------------------------------------------- 1 | # Unsafe doing 2 | -------------------------------------------------------------------------------- /zh-CN/src/variables.md: -------------------------------------------------------------------------------- 1 | # 变量绑定与解构 2 | 3 | ### 绑定和可变性 4 | 1. 🌟 变量只有在初始化后才能被使用 5 | 6 | ```rust,editable 7 | 8 | // 修复下面代码的错误并尽可能少的修改 9 | fn main() { 10 | let x: i32; // 未初始化,但被使用 11 | let y: i32; // 未初始化,也未被使用 12 | println!("x is equal to {}", x); 13 | } 14 | ``` 15 | 16 | 2. 🌟🌟 可以使用 `mut` 将变量标记为可变 17 | ```rust,editable 18 | 19 | // 完形填空,让代码编译 20 | fn main() { 21 | let __ = 1; 22 | __ += 2; 23 | 24 | println!("x = {}", x); 25 | } 26 | ``` 27 | 28 | ### 变量作用域 29 | 3. 🌟 作用域是一个变量在程序中能够保持合法的范围 30 | 31 | ```rust,editable 32 | 33 | // 修复下面代码的错误并使用尽可能少的改变 34 | fn main() { 35 | let x: i32 = 10; 36 | { 37 | let y: i32 = 5; 38 | println!("x 的值是 {}, y 的值是 {}", x, y); 39 | } 40 | println!("x 的值是 {}, y 的值是 {}", x, y); 41 | } 42 | ``` 43 | 44 | 4. 🌟🌟 45 | 46 | ```rust,editable 47 | // 修复错误 48 | fn main() { 49 | println!("{}, world", x); 50 | } 51 | 52 | fn define_x() { 53 | let x = "hello"; 54 | } 55 | ``` 56 | 57 | ### 变量遮蔽( Shadowing ) 58 | 5. 🌟🌟 若后面的变量声明的名称和之前的变量相同,则我们说:第一个变量被第二个同名变量遮蔽了( shadowing ) 59 | 60 | ```rust,editable 61 | 62 | // 只允许修改 `assert_eq!` 来让 `println!` 工作(在终端输出 `42`) 63 | fn main() { 64 | let x: i32 = 5; 65 | { 66 | let x = 12; 67 | assert_eq!(x, 5); 68 | } 69 | 70 | assert_eq!(x, 12); 71 | 72 | let x = 42; 73 | println!("{}", x); // 输出 "42". 74 | } 75 | ``` 76 | 77 | 6. 🌟🌟 修改一行代码以通过编译 78 | ```rust,editable 79 | 80 | fn main() { 81 | let mut x: i32 = 1; 82 | x = 7; 83 | // 遮蔽且再次绑定 84 | let x = x; 85 | x += 3; 86 | 87 | 88 | let y = 4; 89 | // 遮蔽 90 | let y = "I can also be bound to text!"; 91 | } 92 | ``` 93 | 94 | ### 未使用的变量 95 | 7. 使用以下方法来修复编译器输出的 warning : 96 | 97 | - 🌟 一种方法 98 | - 🌟🌟 两种方法 99 | 100 | > 注意: 你可以使用两种方法解决,但是它们没有一种是移除 `let x = 1` 所在的代码行 101 | 102 | ```rust,editable 103 | 104 | fn main() { 105 | let x = 1; 106 | } 107 | 108 | // compiler warning: unused variable: `x` 109 | ``` 110 | 111 | ### 变量解构 112 | 8. 🌟🌟 我们可以将 `let` 跟一个模式一起使用来解构一个元组,最终将它解构为多个独立的变量 113 | 114 | > 提示: 可以使用变量遮蔽或可变性 115 | 116 | ```rust,editable 117 | 118 | // 修复下面代码的错误并尽可能少的修改 119 | fn main() { 120 | let (x, y) = (1, 2); 121 | x += 2; 122 | 123 | assert_eq!(x, 3); 124 | assert_eq!(y, 2); 125 | } 126 | ``` 127 | 128 | ### 解构式赋值 129 | 该功能于 Rust 1.59 版本引入:你可以在赋值语句的左式中使用元组、切片或结构体进行匹配赋值。 130 | 131 | 9. 🌟🌟 132 | 133 | > Note: 解构式赋值只能在 Rust 1.59 或者更高版本中使用 134 | 135 | ```rust,editable 136 | 137 | fn main() { 138 | let (x, y); 139 | (x,..) = (3, 4); 140 | [.., y] = [1, 2]; 141 | // 填空,让代码工作 142 | assert_eq!([x,y], __); 143 | } 144 | ``` 145 | 146 | > [答案](https://github.com/sunface/rust-by-practice/blob/master/solutions/variables.md) 在 solutions 下面 147 | -------------------------------------------------------------------------------- /zh-CN/src/weak.md: -------------------------------------------------------------------------------- 1 | # Weak and Circle reference 2 | -------------------------------------------------------------------------------- /zh-CN/src/why-exercise.md: -------------------------------------------------------------------------------- 1 |

Rust语言实战

2 | 3 |
4 | 5 |
6 | 7 |

通过有挑战性的示例、练习题、实践项目来提升 Rust 水平,建立从入门学习到上手实战的直通桥梁

8 | 9 |
10 | 11 | [![Stars Count](https://img.shields.io/github/stars/sunface/rust-by-practice?style=flat)](https://github.com/sunface/rust-by-practice/stargazers) [![Forks Count](https://img.shields.io/github/forks/sunface/rust-by-practice.svg?style=flat)](https://github.com/naaive/orange/network/members) 12 | [![LICENSE](https://img.shields.io/badge/license-mit-green?style=flat)](https://github.com/sunface/rust-by-practice/blob/master/LICENSE) 13 |
14 | 15 | *Rust语言实战* 的目标是通过大量的实战练习帮助大家更好的学习和上手使用 Rust 语言。书中的练习题非常易于使用:你所需的就是在线完成练习,并让它通过编译。 16 | 17 | 18 | ## 在线阅读 19 | 20 | - [https://zh.practice.rs](https://zh.practice.rs) 21 | 22 | ## 本地运行 23 | 24 | 我们使用 [mdbook](https://rust-lang.github.io/mdBook/) 构建在线练习题,你也可以下载到本地运行: 25 | ```shell 26 | $ git clone https://github.com/sunface/rust-by-practice 27 | $ cargo install mdbook 28 | $ cd rust-by-practice && mdbook serve zh-CN/ 29 | ``` 30 | 在本地win 10或者linux服务器上运行时,应当使用 -n 参数指定mdbook服务所监听的IP地址(-p 参数指定服务监听的端口,不指定则为默认的3000),以win 10本地运行为例: 31 | ```shell 32 | $ mdbook serve -p 8888 -n 127.0.0.1 zh-CN/ 33 | ``` 34 | ## 特性 35 | 36 | 部分示例和习题借鉴了 [Rust By Example](https://github.com/rust-lang/rust-by-example), 书中的示例真的非常棒! 37 | 38 | 尽管它们非常优秀,我们这本书也有自己的秘密武器 :) 39 | 40 | - 每个章节分为三个可选部分:示例、练习和实践项目 41 | 42 | - 除了示例外,我们还有大量的高质量练习题,你可以在线阅读、修改和编译它们 43 | 44 | - 覆盖了 Rust 语言的几乎所有方面:基础语言特性、高级语言特性、async/await 异步编程、多线程、并发原语、性能优化、工具链使用、标准库、数据结构和算法等 45 | 46 | - 每一道练习题都提供了解答 47 | 48 | - 整体难度相对更高,更加贴近于实战难度: 简单 🌟 , 中等 🌟🌟 , 困难 🌟🌟🌟 , 地狱 🌟🌟🌟🌟 49 | 50 | **总之,我们想做的就是解决入门学习后,不知道该如何运用的问题,毕竟对于 Rust 来说,从学习到实战,中间还隔着数个 Go语言 的难度** 51 | 52 | ## 关于我们 53 | 54 | *Rust语言实战* 由 Rust 编程学院倾情打造。 55 | 56 | 同时我们还提供了一本目前最好也是最用心的开源 Rust 书籍 - [Rust语言圣经](https://github.com/sunface/rust-course), 适合从入门到精通所有阶段的学习,欢迎大家阅读使用。 57 | 58 | 对我们来说,来自读者大大的肯定比什么都重要,因此一个 [Github star](https://github.com/sunface/rust-by-practice) 要比一杯咖啡更让我们开心,而且现在它在跳楼打折,无需 998 , 仅需 0 元钱 :) 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /zh-CN/theme/style1.css: -------------------------------------------------------------------------------- 1 | @media only screen and (max-width:1080px) { 2 | .sidetoc { 3 | display: none !important; 4 | } 5 | } 6 | 7 | @media only screen and (min-width:1080px) { 8 | main { 9 | position: relative; 10 | padding-right: 170px; 11 | } 12 | .sidetoc { 13 | margin-left: auto; 14 | margin-right: auto; 15 | /*left: calc(100% + (var(--content-max-width))/4 - 180px);*/ 16 | left: calc(100% - 200px); 17 | position: absolute; 18 | } 19 | .pagetoc { 20 | position: fixed; 21 | width: 200px; 22 | height: calc(100vh - var(--menu-bar-height) - 10rem); 23 | overflow: auto; 24 | z-index: 1000; 25 | } 26 | .pagetoc a { 27 | border-left: 1px solid var(--sidebar-bg); 28 | color: var(--fg) !important; 29 | display: block; 30 | padding-bottom: 5px; 31 | padding-top: 5px; 32 | padding-left: 10px; 33 | text-align: left; 34 | text-decoration: none; 35 | font-size: 1.2rem; 36 | } 37 | .pagetoc a:hover, 38 | .pagetoc a.active { 39 | background: var(--sidebar-bg); 40 | color: var(--sidebar-fg) !important; 41 | } 42 | .pagetoc .active { 43 | background: var(--sidebar-bg); 44 | color: var(--sidebar-fg); 45 | } 46 | } 47 | 48 | .page-footer { 49 | margin-top: 50px; 50 | border-top: 1px solid #ccc; 51 | overflow: hidden; 52 | padding: 10px 0; 53 | color: gray; 54 | } 55 | 56 | /* 修改章节目录的间距 */ 57 | .chapter li.chapter-item { 58 | /* 没有文件时的文字颜色 */ 59 | color: #939da3; 60 | margin-top: 1rem; 61 | } 62 | 63 | /* 修改滚动条宽度 */ 64 | ::-webkit-scrollbar { 65 | width: 5px; 66 | height: 5px; 67 | } 68 | 69 | /* 表格靠左对齐 */ 70 | table { 71 | margin-left: 0 !important; 72 | } 73 | 74 | /* 只使用底部的页面跳转,因为左右两边的宽跳转会被 page-toc 遮盖 */ 75 | @media only screen and (max-width: 2560px) { 76 | .nav-wide-wrapper { display: none; } 77 | .nav-wrapper { display: block; } 78 | } 79 | @media only screen and (max-width: 2560px) { 80 | .sidebar-visible .nav-wide-wrapper { display: none; } 81 | .sidebar-visible .nav-wrapper { display: block; } 82 | } 83 | 84 | /* 修复可编辑代码框顶部过窄的问题 */ 85 | code.editable, .ace_scroller { 86 | top: 10px; 87 | } --------------------------------------------------------------------------------