├── static ├── .nojekyll └── img │ ├── favicon.ico │ ├── social-card.jpg │ ├── logo.svg │ └── undraw_docusaurus_tree.svg ├── FUNDING.yml ├── examples ├── .gitignore ├── ch17-post-state-pattern │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── ch17-post-state-types │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── ch14-workspace │ ├── add_one │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── Cargo.toml │ ├── adder │ │ ├── src │ │ │ └── main.rs │ │ └── Cargo.toml │ ├── add_two │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── Cargo.lock ├── ch19-hello-macro │ ├── hello_macro │ │ ├── src │ │ │ └── lib.rs │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── hello_macro_derive │ │ │ ├── Cargo.toml │ │ │ ├── src │ │ │ └── lib.rs │ │ │ └── Cargo.lock │ └── pancakes │ │ ├── src │ │ └── main.rs │ │ ├── Cargo.toml │ │ └── Cargo.lock ├── ch20-graceful-shutdown │ ├── Cargo.toml │ ├── Cargo.lock │ ├── hello.html │ ├── 404.html │ └── src │ │ ├── main.rs │ │ └── lib.rs ├── ch21-async-web-server │ ├── Cargo.toml │ ├── hello.html │ ├── 404.html │ ├── src │ │ └── main.rs │ └── Cargo.lock ├── ch12-minigrep │ ├── Cargo.lock │ ├── Cargo.toml │ ├── poem.txt │ └── src │ │ ├── main.rs │ │ └── lib.rs ├── ch13-minigrep │ ├── Cargo.lock │ ├── Cargo.toml │ ├── poem.txt │ └── src │ │ ├── main.rs │ │ └── lib.rs ├── ch20-multi-threaded-web-server │ ├── Cargo.lock │ ├── Cargo.toml │ ├── hello.html │ ├── 404.html │ └── src │ │ ├── main.rs │ │ └── lib.rs └── ch20-single-threaded-web-server │ ├── Cargo.lock │ ├── Cargo.toml │ ├── hello.html │ ├── 404.html │ └── src │ └── main.rs ├── README.md ├── babel.config.js ├── renovate.json ├── .markdownlint.json ├── docs ├── zz-appendix │ ├── _category_.json │ ├── appendix-05-editions.md │ ├── appendix-01-keywords.md │ ├── appendix-04-useful-development-tools.md │ ├── appendix-03-derivable-traits.md │ └── appendix-02-operators.md ├── ch19 │ ├── _category_.json │ ├── ch19-04-advanced-functions-and-closures.md │ ├── ch19-03-advanced-types.md │ ├── ch19-05-macros.md │ └── ch19-01-unsafe.md ├── ch10 │ ├── _category_.json │ └── ch10-01-generic-data-types.md ├── ch20 │ ├── _category_.json │ ├── ch20-01-single-threaded-web-server.md │ ├── ch20-03-graceful-shutdown.md │ └── ch20-02-multi-threaded-web-server.md ├── ch01-getting-started.md ├── ch00-intro.md ├── ch05-structs.md ├── ch06-enums-and-pattern-matching.md └── ch21-async.md ├── sidebars.js ├── .gitignore ├── package.json ├── .vscode └── settings.json ├── src └── css │ └── custom.css ├── CONTRIBUTING.md ├── docusaurus.config.js └── .github └── workflows └── deploy-gh-pages.yml /static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [jwalton] -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | target -------------------------------------------------------------------------------- /examples/ch17-post-state-pattern/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /examples/ch17-post-state-types/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /examples/ch14-workspace/add_one/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn add_one(x: i32) -> i32 { 2 | x + 1 3 | } -------------------------------------------------------------------------------- /examples/ch19-hello-macro/hello_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub trait HelloMacro { 2 | fn hello_macro(); 3 | } -------------------------------------------------------------------------------- /static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwalton/rust-book-abridged/HEAD/static/img/favicon.ico -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Rust Book (Abridged) 2 | 3 | Read the book [here](https://jasonwalton.ca/rust-book-abridged/). -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /static/img/social-card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwalton/rust-book-abridged/HEAD/static/img/social-card.jpg -------------------------------------------------------------------------------- /examples/ch14-workspace/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "adder", 5 | "add_one", 6 | "add_two", 7 | ] -------------------------------------------------------------------------------- /examples/ch20-graceful-shutdown/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /examples/ch14-workspace/add_one/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "add_one" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rand = "0.8.5" 8 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "line-length": false, 3 | "commands-show-output": false, 4 | "no-trailing-punctuation": false, 5 | "no-inline-html": false 6 | } -------------------------------------------------------------------------------- /examples/ch14-workspace/adder/src/main.rs: -------------------------------------------------------------------------------- 1 | use add_one; 2 | 3 | fn main() { 4 | let num = 10; 5 | println!("Hello, world! {num} plus one is {}!", add_one::add_one(num)); 6 | } -------------------------------------------------------------------------------- /docs/zz-appendix/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "collapsed": true, 3 | "label": "Appendix", 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Appendix" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/ch14-workspace/adder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "adder" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rand = "0.8.5" 8 | add_one = { path = "../add_one" } -------------------------------------------------------------------------------- /examples/ch21-async-web-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-async" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1", features = ["full"] } 8 | -------------------------------------------------------------------------------- /docs/ch19/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "collapsed": false, 3 | "label": "19 - Advanced Features", 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Advanced Features" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/ch12-minigrep/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "minigrep" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /examples/ch13-minigrep/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "minigrep" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /examples/ch20-graceful-shutdown/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /examples/ch20-multi-threaded-web-server/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /examples/ch20-single-threaded-web-server/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /examples/ch19-hello-macro/hello_macro/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello_macro" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /examples/ch19-hello-macro/hello_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello_macro" 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 | -------------------------------------------------------------------------------- /examples/ch12-minigrep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minigrep" 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 | -------------------------------------------------------------------------------- /examples/ch13-minigrep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minigrep" 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 | -------------------------------------------------------------------------------- /sidebars.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 4 | const sidebars = { 5 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 6 | }; 7 | 8 | module.exports = sidebars; 9 | -------------------------------------------------------------------------------- /docs/ch10/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "collapsed": false, 3 | "label": "10 - Generic Types, Traits, and Lifetimes", 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Generic Types, Traits, and Lifetimes" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/ch20/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "collapsed": false, 3 | "label": "20 - Multithreaded Web Server", 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Final Project: Building a Multithreaded Web Server" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/ch17-post-state-pattern/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "post" 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 | -------------------------------------------------------------------------------- /examples/ch17-post-state-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "post" 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 | -------------------------------------------------------------------------------- /examples/ch14-workspace/add_two/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "add_two" 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 | -------------------------------------------------------------------------------- /examples/ch20-multi-threaded-web-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello" 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 | -------------------------------------------------------------------------------- /examples/ch20-single-threaded-web-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello" 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 | -------------------------------------------------------------------------------- /examples/ch19-hello-macro/hello_macro/hello_macro_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello_macro_derive" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | proc-macro = true 8 | 9 | [dependencies] 10 | syn = "1.0" 11 | quote = "1.0" 12 | -------------------------------------------------------------------------------- /examples/ch20-graceful-shutdown/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello! 6 | 7 | 8 |

Hello!

9 |

Hi from Rust

10 | 11 | -------------------------------------------------------------------------------- /examples/ch21-async-web-server/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello! 6 | 7 | 8 |

Hello!

9 |

Hi from Rust

10 | 11 | -------------------------------------------------------------------------------- /examples/ch20-multi-threaded-web-server/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello! 6 | 7 | 8 |

Hello!

9 |

Hi from Rust

10 | 11 | -------------------------------------------------------------------------------- /examples/ch20-single-threaded-web-server/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello! 6 | 7 | 8 |

Hello!

9 |

Hi from Rust

10 | 11 | -------------------------------------------------------------------------------- /examples/ch12-minigrep/poem.txt: -------------------------------------------------------------------------------- 1 | I'm nobody! Who are you? 2 | Are you nobody, too? 3 | Then there's a pair of us - don't tell! 4 | They'd banish us, you know. 5 | 6 | How dreary to be somebody! 7 | How public, like a frog 8 | To tell your name the livelong day 9 | To an admiring bog! 10 | -------------------------------------------------------------------------------- /examples/ch13-minigrep/poem.txt: -------------------------------------------------------------------------------- 1 | I'm nobody! Who are you? 2 | Are you nobody, too? 3 | Then there's a pair of us - don't tell! 4 | They'd banish us, you know. 5 | 6 | How dreary to be somebody! 7 | How public, like a frog 8 | To tell your name the livelong day 9 | To an admiring bog! 10 | -------------------------------------------------------------------------------- /examples/ch20-graceful-shutdown/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello! 6 | 7 | 8 |

Oops!

9 |

Sorry, I don't know what you're asking for.

10 | 11 | -------------------------------------------------------------------------------- /examples/ch21-async-web-server/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello! 6 | 7 | 8 |

Oops!

9 |

Sorry, I don't know what you're asking for.

10 | 11 | -------------------------------------------------------------------------------- /examples/ch20-multi-threaded-web-server/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello! 6 | 7 | 8 |

Oops!

9 |

Sorry, I don't know what you're asking for.

10 | 11 | -------------------------------------------------------------------------------- /examples/ch20-single-threaded-web-server/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello! 6 | 7 | 8 |

Oops!

9 |

Sorry, I don't know what you're asking for.

10 | 11 | -------------------------------------------------------------------------------- /examples/ch14-workspace/add_two/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn add(left: usize, right: usize) -> usize { 2 | left + right 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | use super::*; 8 | 9 | #[test] 10 | fn it_works() { 11 | let result = add(2, 2); 12 | assert_eq!(result, 4); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /examples/ch19-hello-macro/pancakes/src/main.rs: -------------------------------------------------------------------------------- 1 | use hello_macro::HelloMacro; 2 | use hello_macro_derive::HelloMacro; 3 | 4 | // This derive attribute will run our derive macro. 5 | #[derive(HelloMacro)] 6 | struct Pancakes; 7 | 8 | fn main() { 9 | // This will print "Hello, Macro! My name is Pancakes!" 10 | Pancakes::hello_macro(); 11 | } -------------------------------------------------------------------------------- /examples/ch19-hello-macro/pancakes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pancakes" 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 | hello_macro = { path = "../hello_macro" } 10 | hello_macro_derive = { path = "../hello_macro/hello_macro_derive" } -------------------------------------------------------------------------------- /examples/ch13-minigrep/src/main.rs: -------------------------------------------------------------------------------- 1 | use minigrep::Config; 2 | use std::{env, process}; 3 | 4 | fn main() { 5 | let config = Config::build(env::args()).unwrap_or_else(|err| { 6 | eprintln!("Problem parsing arguments: {err}"); 7 | process::exit(1); 8 | }); 9 | 10 | if let Err(e) = minigrep::run(config) { 11 | println!("Application error: {e}"); 12 | process::exit(1); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/ch12-minigrep/src/main.rs: -------------------------------------------------------------------------------- 1 | use minigrep::Config; 2 | use std::{env, process}; 3 | 4 | fn main() { 5 | let args: Vec = env::args().collect(); 6 | 7 | let config = Config::build(&args).unwrap_or_else(|err| { 8 | println!("Problem parsing arguments: {err}"); 9 | process::exit(1); 10 | }); 11 | 12 | if let Err(e) = minigrep::run(config) { 13 | println!("Application error: {e}"); 14 | process::exit(1); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/ch19-hello-macro/hello_macro/hello_macro_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | use syn; 4 | 5 | // This line tells Rust that this is the macro 6 | // to call when someone does `#[derive(HelloMacro)]`. 7 | #[proc_macro_derive(HelloMacro)] 8 | pub fn hello_macro_derive(input: TokenStream) -> TokenStream { 9 | // Construct a representation of Rust code as a syntax tree 10 | // that we can manipulate 11 | let ast = syn::parse(input).unwrap(); 12 | 13 | // Build the trait implementation 14 | impl_hello_macro(&ast) 15 | } 16 | 17 | // It's very common to split the derive macro into one function 18 | // that parses the input (`hello_macro_derive`) and one that 19 | // generates the code (`impl_hello_macro`). 20 | fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream { 21 | let name = &ast.ident; 22 | let gen = quote! { 23 | impl HelloMacro for #name { 24 | fn hello_macro() { 25 | println!("Hello, Macro! My name is {}!", stringify!(#name)); 26 | } 27 | } 28 | }; 29 | 30 | // Convert `gen` into a `TokenStream`. 31 | gen.into() 32 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rust-book-abridged", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "2.4.0", 18 | "@docusaurus/preset-classic": "2.4.0", 19 | "@mdx-js/react": "^1.6.22", 20 | "clsx": "^1.2.1", 21 | "prism-react-renderer": "^1.3.5", 22 | "react": "^17.0.2", 23 | "react-dom": "^17.0.2" 24 | }, 25 | "devDependencies": { 26 | "@docusaurus/module-type-aliases": "2.4.0" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.5%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | }, 40 | "engines": { 41 | "node": ">=16.14" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "Clippy", 4 | "codepoints", 5 | "consts", 6 | "debuginfo", 7 | "eprintln", 8 | "goroutines", 9 | "hasher", 10 | "isize", 11 | "Klabnik", 12 | "malloc", 13 | "memcpy", 14 | "metaprogramming", 15 | "metavariable", 16 | "metavariables", 17 | "millis", 18 | "monomorphization", 19 | "mpsc", 20 | "multibyte", 21 | "myprogram", 22 | "newtype", 23 | "Nonequality", 24 | "overloadable", 25 | "powi", 26 | "println", 27 | "RAII", 28 | "repr", 29 | "rustc", 30 | "rustfix", 31 | "rustfmt", 32 | "Rustonomicon", 33 | "rustup", 34 | "SIGINT", 35 | "SIGTERM", 36 | "structs", 37 | "subfolders", 38 | "Subtyping", 39 | "supertrait", 40 | "supertraits", 41 | "tlsv", 42 | "Toonie", 43 | "turbofish", 44 | "typeof", 45 | "unergonomic", 46 | "unoptimized", 47 | "unrepresentable", 48 | "unsized", 49 | "usize", 50 | "vals", 51 | "waitlist" 52 | ] 53 | } -------------------------------------------------------------------------------- /examples/ch17-post-state-types/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub struct Post { 2 | content: String, 3 | } 4 | 5 | pub struct DraftPost { 6 | content: String, 7 | } 8 | 9 | impl Post { 10 | pub fn new() -> DraftPost { 11 | DraftPost { 12 | content: String::new(), 13 | } 14 | } 15 | 16 | pub fn content(&self) -> &str { 17 | &self.content 18 | } 19 | } 20 | 21 | impl DraftPost { 22 | pub fn add_text(&mut self, text: &str) { 23 | self.content.push_str(text); 24 | } 25 | 26 | pub fn request_review(self) -> PendingReviewPost { 27 | PendingReviewPost { 28 | content: self.content, 29 | } 30 | } 31 | } 32 | 33 | pub struct PendingReviewPost { 34 | content: String, 35 | } 36 | 37 | impl PendingReviewPost { 38 | pub fn approve(self) -> Post { 39 | Post { 40 | content: self.content, 41 | } 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use super::*; 48 | 49 | #[test] 50 | fn post_workflow() { 51 | let mut post = Post::new(); 52 | 53 | post.add_text("I ate a salad for lunch today"); 54 | 55 | let post = post.request_review(); 56 | 57 | let post = post.approve(); 58 | assert_eq!("I ate a salad for lunch today", post.content()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/ch19-hello-macro/hello_macro/hello_macro_derive/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello_macro_derive" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "quote", 10 | "syn", 11 | ] 12 | 13 | [[package]] 14 | name = "proc-macro2" 15 | version = "1.0.56" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 18 | dependencies = [ 19 | "unicode-ident", 20 | ] 21 | 22 | [[package]] 23 | name = "quote" 24 | version = "1.0.26" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 27 | dependencies = [ 28 | "proc-macro2", 29 | ] 30 | 31 | [[package]] 32 | name = "syn" 33 | version = "1.0.109" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 36 | dependencies = [ 37 | "proc-macro2", 38 | "quote", 39 | "unicode-ident", 40 | ] 41 | 42 | [[package]] 43 | name = "unicode-ident" 44 | version = "1.0.8" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 47 | -------------------------------------------------------------------------------- /src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary-darkest: #521010; 10 | --ifm-color-primary-darker: #5f1414; 11 | --ifm-color-primary-dark: #6c1919; 12 | --ifm-color-primary: #792020; 13 | --ifm-color-primary-light: #862a2a; 14 | --ifm-color-primary-lighter: #933939; 15 | --ifm-color-primary-lightest: #a14c4c; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary-darkest: #bd6060; 23 | --ifm-color-primary-darker: #c86767; 24 | --ifm-color-primary-dark: #d37070; 25 | --ifm-color-primary: #de7b7b; 26 | --ifm-color-primary-light: #e98989; 27 | --ifm-color-primary-lighter: #f49c9c; 28 | --ifm-color-primary-lightest: #ffb3b3; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | 32 | /* This works around the issue of truncated code-blocks in https://github.com/signcl/docusaurus-prince-pdf/ */ 33 | @media print { 34 | code[class*='codeBlockLines'] { 35 | float: none; 36 | display: block; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/ch19-hello-macro/pancakes/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello_macro" 7 | version = "0.1.0" 8 | 9 | [[package]] 10 | name = "hello_macro_derive" 11 | version = "0.1.0" 12 | dependencies = [ 13 | "quote", 14 | "syn", 15 | ] 16 | 17 | [[package]] 18 | name = "pancakes" 19 | version = "0.1.0" 20 | dependencies = [ 21 | "hello_macro", 22 | "hello_macro_derive", 23 | ] 24 | 25 | [[package]] 26 | name = "proc-macro2" 27 | version = "1.0.56" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 30 | dependencies = [ 31 | "unicode-ident", 32 | ] 33 | 34 | [[package]] 35 | name = "quote" 36 | version = "1.0.26" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 39 | dependencies = [ 40 | "proc-macro2", 41 | ] 42 | 43 | [[package]] 44 | name = "syn" 45 | version = "1.0.109" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 48 | dependencies = [ 49 | "proc-macro2", 50 | "quote", 51 | "unicode-ident", 52 | ] 53 | 54 | [[package]] 55 | name = "unicode-ident" 56 | version = "1.0.8" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 59 | -------------------------------------------------------------------------------- /examples/ch20-single-threaded-web-server/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs, 3 | io::{prelude::*, BufReader}, 4 | net::{TcpListener, TcpStream}, 5 | }; 6 | 7 | fn main() { 8 | let port = 7878u16; 9 | let listen_address = format!("127.0.0.1:{port}"); 10 | let listener = TcpListener::bind(listen_address).unwrap(); 11 | 12 | println!("Listening on port {}", port); 13 | 14 | for stream in listener.incoming() { 15 | let stream = stream.unwrap(); 16 | 17 | handle_connection(stream); 18 | } 19 | } 20 | 21 | fn handle_connection(mut stream: TcpStream) { 22 | let buf_reader = BufReader::new(&mut stream); 23 | 24 | // A line could be an error if it contains invalid 25 | // UTF-8, or if there's a problem reading from the 26 | // underlying stream. We ignore these errors here. 27 | let http_request: Vec<_> = buf_reader 28 | .lines() 29 | .map(|result| result.unwrap()) 30 | .take_while(|line| !line.is_empty()) // Blank line is end of headers. 31 | .collect(); 32 | 33 | let request_line = &http_request[0]; 34 | 35 | println!("Incoming request for {}", request_line); 36 | 37 | if request_line == "GET / HTTP/1.1" { 38 | send_response(stream, 200, "OK", "hello.html"); 39 | } else { 40 | send_response(stream, 404, "NOT FOUND", "404.html"); 41 | } 42 | } 43 | 44 | fn send_response(mut stream: TcpStream, code: u16, reason: &str, filename: &str) { 45 | let contents = fs::read_to_string(filename).unwrap(); 46 | let length = contents.len(); 47 | let response = 48 | format!("HTTP/1.1 {code} {reason}\r\nContent-Length: {length}\r\n\r\n{contents}"); 49 | 50 | stream.write_all(response.as_bytes()).unwrap(); 51 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Please feel free to raise an issue or a pull request if you see something that needs fixing. 4 | 5 | ## Conventions Used in This Book 6 | 7 | If not specified here, try to follow the style of "The Rust Programming Language". 8 | 9 | Where possible, examples are copy-pasted directly from the original "The Rust Programming Language". We don't add listing numbers to our listings unless we want to reference them later in the chapter. 10 | 11 | For consistency, when talking about the language "Rust" we capitalize the first letter (never "rust"). In many places we refer to other languages in this book. For consistency, we use "JavaScript", "TypeScript", "Java", "C", "C++", "Python", and "Go". 12 | 13 | Chapter and section numbers are written using a heading and a hyphen: 14 | 15 | ```md 16 | # 2 - Chapter 2 title goes here 17 | ## 2.1 - Section 2.1 title goes here 18 | ### A subsection of 2.1 19 | ``` 20 | 21 | We generally don't go more than three levels deep - if the original Rust book has something that would be a fourth-level header, we either just drop it or convert it to a third level header (this is supposed to be the abridged version, after all). 22 | 23 | When linking to another chapter, we use the convention `[chapter 10][chap10]`. All chapter links required are specified at the bottom of the file. All chapter links are available in the `ch00-intro.md` file as well, so if any are missing in a given chapter, they can be copy-pasted from there. Links to a specific section just link to the section. For example: 24 | 25 | ```md 26 | [`Option` enum](./ch06-enums-and-pattern-matching.md#the-option-enum-and-its-advantages-over-null-values) 27 | ``` 28 | 29 | File names, like _lib.rs_ should be italicized. 30 | 31 | All .md files should pass markdownlint. 32 | -------------------------------------------------------------------------------- /examples/ch21-async-web-server/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, time::Duration}; 2 | use tokio::{ 3 | fs, 4 | io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, 5 | net::{TcpListener, TcpStream}, 6 | }; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<(), Box> { 10 | let port = 7878u16; 11 | let listen_address = format!("127.0.0.1:{port}"); 12 | let listener = TcpListener::bind(listen_address).await.unwrap(); 13 | println!("Listening on port {}", port); 14 | 15 | loop { 16 | let (stream, _) = listener.accept().await.unwrap(); 17 | tokio::spawn(async move { 18 | handle_connection(stream).await; 19 | }); 20 | } 21 | } 22 | 23 | async fn handle_connection(mut stream: TcpStream) { 24 | let buf_reader = BufReader::new(&mut stream); 25 | 26 | let mut lines = buf_reader.lines(); 27 | let request_line = lines.next_line().await.unwrap().unwrap(); 28 | 29 | println!("Incoming request for {}", request_line); 30 | 31 | match &request_line[..] { 32 | "GET / HTTP/1.1" => send_response(stream, 200, "OK", "hello.html").await, 33 | "GET /sleep HTTP/1.1" => { 34 | tokio::time::sleep(Duration::from_secs(5)).await; 35 | send_response(stream, 200, "OK", "hello.html").await; 36 | } 37 | _ => send_response(stream, 404, "NOT FOUND", "404.html").await, 38 | } 39 | } 40 | 41 | async fn send_response(mut stream: TcpStream, code: u16, reason: &str, filename: &str) { 42 | let contents = fs::read_to_string(filename).await.unwrap(); 43 | let length = contents.len(); 44 | let response = 45 | format!("HTTP/1.1 {code} {reason}\r\nContent-Length: {length}\r\n\r\n{contents}"); 46 | 47 | stream.write_all(response.as_bytes()).await.unwrap(); 48 | } 49 | -------------------------------------------------------------------------------- /examples/ch20-graceful-shutdown/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs, 3 | io::{prelude::*, BufReader}, 4 | net::{TcpListener, TcpStream}, 5 | thread, 6 | time::Duration, 7 | }; 8 | 9 | use hello::ThreadPool; 10 | 11 | fn main() { 12 | let port = 7878u16; 13 | let listen_address = format!("127.0.0.1:{port}"); 14 | let listener = TcpListener::bind(listen_address).unwrap(); 15 | println!("Listening on port {}", port); 16 | 17 | let pool = ThreadPool::new(4); 18 | for stream in listener.incoming().take(2) { 19 | let stream = stream.unwrap(); 20 | 21 | pool.execute(|| { 22 | handle_connection(stream); 23 | }); 24 | } 25 | } 26 | 27 | fn handle_connection(mut stream: TcpStream) { 28 | let buf_reader = BufReader::new(&mut stream); 29 | 30 | // A line could be an error if it contains invalid 31 | // UTF-8, or if there's a problem reading from the 32 | // underlying stream. We ignore these errors here. 33 | let http_request: Vec<_> = buf_reader 34 | .lines() 35 | .map(|result| result.unwrap()) 36 | .take_while(|line| !line.is_empty()) // Blank line is end of headers. 37 | .collect(); 38 | 39 | let request_line = &http_request[0]; 40 | 41 | println!("Incoming request for {}", request_line); 42 | 43 | match &request_line[..] { 44 | "GET / HTTP/1.1" => send_response(stream, 200, "OK", "hello.html"), 45 | "GET /sleep HTTP/1.1" => { 46 | thread::sleep(Duration::from_secs(5)); 47 | send_response(stream, 200, "OK", "hello.html"); 48 | } 49 | _ => send_response(stream, 404, "NOT FOUND", "404.html"), 50 | } 51 | } 52 | 53 | fn send_response(mut stream: TcpStream, code: u16, reason: &str, filename: &str) { 54 | let contents = fs::read_to_string(filename).unwrap(); 55 | let length = contents.len(); 56 | let response = 57 | format!("HTTP/1.1 {code} {reason}\r\nContent-Length: {length}\r\n\r\n{contents}"); 58 | 59 | stream.write_all(response.as_bytes()).unwrap(); 60 | } 61 | -------------------------------------------------------------------------------- /examples/ch20-multi-threaded-web-server/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs, 3 | io::{prelude::*, BufReader}, 4 | net::{TcpListener, TcpStream}, 5 | thread, 6 | time::Duration, 7 | }; 8 | 9 | use hello::ThreadPool; 10 | 11 | fn main() { 12 | let port = 7878u16; 13 | let listen_address = format!("127.0.0.1:{port}"); 14 | let listener = TcpListener::bind(listen_address).unwrap(); 15 | println!("Listening on port {}", port); 16 | 17 | let pool = ThreadPool::new(4); 18 | for stream in listener.incoming() { 19 | let stream = stream.unwrap(); 20 | 21 | pool.execute(|| { 22 | handle_connection(stream); 23 | }); 24 | } 25 | } 26 | 27 | fn handle_connection(mut stream: TcpStream) { 28 | let buf_reader = BufReader::new(&mut stream); 29 | 30 | // A line could be an error if it contains invalid 31 | // UTF-8, or if there's a problem reading from the 32 | // underlying stream. We ignore these errors here. 33 | let http_request: Vec<_> = buf_reader 34 | .lines() 35 | .map(|result| result.unwrap()) 36 | .take_while(|line| !line.is_empty()) // Blank line is end of headers. 37 | .collect(); 38 | 39 | let request_line = &http_request[0]; 40 | 41 | println!("Incoming request for {}", request_line); 42 | 43 | match &request_line[..] { 44 | "GET / HTTP/1.1" => send_response(stream, 200, "OK", "hello.html"), 45 | "GET /sleep HTTP/1.1" => { 46 | thread::sleep(Duration::from_secs(5)); 47 | send_response(stream, 200, "OK", "hello.html"); 48 | } 49 | _ => send_response(stream, 404, "NOT FOUND", "404.html"), 50 | } 51 | } 52 | 53 | fn send_response(mut stream: TcpStream, code: u16, reason: &str, filename: &str) { 54 | let contents = fs::read_to_string(filename).unwrap(); 55 | let length = contents.len(); 56 | let response = 57 | format!("HTTP/1.1 {code} {reason}\r\nContent-Length: {length}\r\n\r\n{contents}"); 58 | 59 | stream.write_all(response.as_bytes()).unwrap(); 60 | } 61 | -------------------------------------------------------------------------------- /examples/ch20-multi-threaded-web-server/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | sync::{mpsc, Arc, Mutex}, 3 | thread::{self, JoinHandle}, 4 | }; 5 | 6 | type Job = Box; 7 | 8 | pub struct ThreadPool { 9 | workers: Vec, 10 | sender: mpsc::Sender, 11 | } 12 | 13 | impl ThreadPool { 14 | /// Create a new ThreadPool. 15 | /// 16 | /// The size is the number of threads in the pool. 17 | /// 18 | /// # Panics 19 | /// 20 | /// The `new` function will panic if the size is zero. 21 | pub fn new(size: usize) -> ThreadPool { 22 | // Make sure `size` is valid. 23 | assert!(size > 0); 24 | 25 | // Create our sender and receiver 26 | let (sender, receiver) = mpsc::channel(); 27 | let receiver = Arc::new(Mutex::new(receiver)); 28 | 29 | // Create a new vector. Pre-allocate the vector 30 | // to be of length `size` so we know it can store 31 | // all of our threads. 32 | let mut workers = Vec::with_capacity(size); 33 | 34 | // Create new workers and add them to the pool. 35 | for id in 0..size { 36 | workers.push(Worker::new(id, Arc::clone(&receiver))); 37 | } 38 | 39 | ThreadPool { 40 | workers, 41 | sender, 42 | } 43 | } 44 | 45 | pub fn execute(&self, f: F) 46 | where 47 | F: FnOnce() + Send + 'static, 48 | { 49 | // Send our job to a Worker. 50 | let job = Box::new(f); 51 | self.sender.send(job).unwrap(); 52 | } 53 | } 54 | 55 | struct Worker { 56 | id: usize, 57 | thread: JoinHandle<()>, 58 | } 59 | 60 | impl Worker { 61 | /// Create a new Worker with the given id. 62 | pub fn new(id: usize, receiver: Arc>>) -> Worker { 63 | let thread = thread::spawn(move || loop { 64 | let job = receiver.lock().unwrap().recv().unwrap(); 65 | println!("Worker {id} got a job; executing."); 66 | job(); 67 | }); 68 | 69 | Worker { id, thread } 70 | } 71 | } -------------------------------------------------------------------------------- /examples/ch14-workspace/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "add_one" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "rand", 10 | ] 11 | 12 | [[package]] 13 | name = "add_two" 14 | version = "0.1.0" 15 | 16 | [[package]] 17 | name = "adder" 18 | version = "0.1.0" 19 | dependencies = [ 20 | "add_one", 21 | "rand", 22 | ] 23 | 24 | [[package]] 25 | name = "cfg-if" 26 | version = "1.0.0" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 29 | 30 | [[package]] 31 | name = "getrandom" 32 | version = "0.2.9" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" 35 | dependencies = [ 36 | "cfg-if", 37 | "libc", 38 | "wasi", 39 | ] 40 | 41 | [[package]] 42 | name = "libc" 43 | version = "0.2.141" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" 46 | 47 | [[package]] 48 | name = "ppv-lite86" 49 | version = "0.2.17" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 52 | 53 | [[package]] 54 | name = "rand" 55 | version = "0.8.5" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 58 | dependencies = [ 59 | "libc", 60 | "rand_chacha", 61 | "rand_core", 62 | ] 63 | 64 | [[package]] 65 | name = "rand_chacha" 66 | version = "0.3.1" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 69 | dependencies = [ 70 | "ppv-lite86", 71 | "rand_core", 72 | ] 73 | 74 | [[package]] 75 | name = "rand_core" 76 | version = "0.6.4" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 79 | dependencies = [ 80 | "getrandom", 81 | ] 82 | 83 | [[package]] 84 | name = "wasi" 85 | version = "0.11.0+wasi-snapshot-preview1" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 88 | -------------------------------------------------------------------------------- /examples/ch13-minigrep/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{env, error::Error, fs}; 2 | 3 | pub struct Config { 4 | pub query: String, 5 | pub file_path: String, 6 | pub ignore_case: bool, 7 | } 8 | 9 | impl Config { 10 | pub fn build(mut args: impl Iterator) -> Result { 11 | args.next(); 12 | 13 | let query = match args.next() { 14 | Some(arg) => arg, 15 | None => return Err("Didn't get a query string"), 16 | }; 17 | 18 | let file_path = match args.next() { 19 | Some(arg) => arg, 20 | None => return Err("Didn't get a file path"), 21 | }; 22 | 23 | let ignore_case = env::var("IGNORE_CASE").is_ok(); 24 | 25 | Ok(Config { 26 | query, 27 | file_path, 28 | ignore_case, 29 | }) 30 | } 31 | } 32 | 33 | pub fn run(config: Config) -> Result<(), Box> { 34 | let contents = fs::read_to_string(config.file_path)?; 35 | 36 | let results = if config.ignore_case { 37 | search_case_insensitive(&config.query, &contents) 38 | } else { 39 | search(&config.query, &contents) 40 | }; 41 | 42 | for line in results { 43 | println!("{line}"); 44 | } 45 | 46 | Ok(()) 47 | } 48 | 49 | pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { 50 | contents 51 | .lines() 52 | .filter(|line| line.contains(query)) 53 | .collect() 54 | } 55 | 56 | pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { 57 | let query = query.to_lowercase(); 58 | contents 59 | .lines() 60 | .filter(|line| line.to_lowercase().contains(&query)) 61 | .collect() 62 | } 63 | 64 | #[cfg(test)] 65 | mod tests { 66 | use super::*; 67 | 68 | #[test] 69 | fn one_result() { 70 | let query = "duct"; 71 | let contents = "\ 72 | Rust: 73 | safe, fast, productive. 74 | Pick three. 75 | Duct tape."; 76 | 77 | assert_eq!(vec!["safe, fast, productive."], search(query, contents)); 78 | } 79 | 80 | #[test] 81 | fn case_insensitive() { 82 | let query = "rUsT"; 83 | let contents = "\ 84 | Rust: 85 | safe, fast, productive. 86 | Pick three. 87 | Trust me."; 88 | 89 | assert_eq!( 90 | vec!["Rust:", "Trust me."], 91 | search_case_insensitive(query, contents) 92 | ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /examples/ch12-minigrep/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, fs, env}; 2 | 3 | pub struct Config { 4 | pub query: String, 5 | pub file_path: String, 6 | pub ignore_case: bool, 7 | } 8 | 9 | impl Config { 10 | pub fn build(args: &[String]) -> Result { 11 | if args.len() < 3 { 12 | return Err("not enough arguments"); 13 | } 14 | 15 | let query = args[1].clone(); 16 | let file_path = args[2].clone(); 17 | 18 | let ignore_case = env::var("IGNORE_CASE").is_ok(); 19 | 20 | Ok(Config { 21 | query, 22 | file_path, 23 | ignore_case, 24 | }) 25 | } 26 | } 27 | 28 | pub fn run(config: Config) -> Result<(), Box> { 29 | let contents = fs::read_to_string(config.file_path)?; 30 | 31 | let results = if config.ignore_case { 32 | search_case_insensitive(&config.query, &contents) 33 | } else { 34 | search(&config.query, &contents) 35 | }; 36 | 37 | for line in results { 38 | println!("{line}"); 39 | } 40 | 41 | Ok(()) 42 | } 43 | 44 | pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { 45 | let mut results = Vec::new(); 46 | 47 | for line in contents.lines() { 48 | if line.contains(query) { 49 | results.push(line); 50 | } 51 | } 52 | 53 | results 54 | } 55 | 56 | pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { 57 | let query = query.to_lowercase(); 58 | let mut results = Vec::new(); 59 | 60 | for line in contents.lines() { 61 | if line.to_lowercase().contains(&query) { 62 | results.push(line); 63 | } 64 | } 65 | 66 | results 67 | } 68 | 69 | #[cfg(test)] 70 | mod tests { 71 | use super::*; 72 | 73 | #[test] 74 | fn one_result() { 75 | let query = "duct"; 76 | let contents = "\ 77 | Rust: 78 | safe, fast, productive. 79 | Pick three. 80 | Duct tape."; 81 | 82 | assert_eq!(vec!["safe, fast, productive."], search(query, contents)); 83 | } 84 | 85 | #[test] 86 | fn case_insensitive() { 87 | let query = "rUsT"; 88 | let contents = "\ 89 | Rust: 90 | safe, fast, productive. 91 | Pick three. 92 | Trust me."; 93 | 94 | assert_eq!( 95 | vec!["Rust:", "Trust me."], 96 | search_case_insensitive(query, contents) 97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /examples/ch17-post-state-pattern/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub struct Post { 2 | state: Option>, 3 | content: String, 4 | } 5 | 6 | impl Post { 7 | pub fn new() -> Post { 8 | Post { 9 | state: Some(Box::new(Draft {})), 10 | content: String::new(), 11 | } 12 | } 13 | 14 | pub fn add_text(&mut self, text: &str) { 15 | self.content.push_str(text); 16 | } 17 | 18 | pub fn request_review(&mut self) { 19 | if let Some(s) = self.state.take() { 20 | self.state = Some(s.request_review()) 21 | } 22 | } 23 | 24 | pub fn approve(&mut self) { 25 | if let Some(s) = self.state.take() { 26 | self.state = Some(s.approve()) 27 | } 28 | } 29 | 30 | pub fn content(&self) -> &str { 31 | self.state.as_ref().unwrap().content(self) 32 | } 33 | } 34 | 35 | trait State { 36 | fn request_review(self: Box) -> Box; 37 | fn approve(self: Box) -> Box; 38 | fn content<'a>(&self, _post: &'a Post) -> &'a str { 39 | "" 40 | } 41 | } 42 | 43 | struct Draft {} 44 | 45 | impl State for Draft { 46 | fn request_review(self: Box) -> Box { 47 | Box::new(PendingReview {}) 48 | } 49 | 50 | fn approve(self: Box) -> Box { 51 | self 52 | } 53 | } 54 | 55 | struct PendingReview {} 56 | 57 | impl State for PendingReview { 58 | fn request_review(self: Box) -> Box { 59 | self 60 | } 61 | 62 | fn approve(self: Box) -> Box { 63 | Box::new(Published {}) 64 | } 65 | } 66 | 67 | struct Published {} 68 | 69 | impl State for Published { 70 | fn request_review(self: Box) -> Box { 71 | self 72 | } 73 | 74 | fn approve(self: Box) -> Box { 75 | self 76 | } 77 | 78 | fn content<'a>(&self, post: &'a Post) -> &'a str { 79 | &post.content 80 | } 81 | } 82 | 83 | #[cfg(test)] 84 | mod tests { 85 | use super::*; 86 | 87 | #[test] 88 | fn post_workflow() { 89 | let mut post = Post::new(); 90 | 91 | post.add_text("I ate a salad for lunch today"); 92 | assert_eq!("", post.content()); 93 | 94 | post.request_review(); 95 | assert_eq!("", post.content()); 96 | 97 | post.approve(); 98 | assert_eq!("I ate a salad for lunch today", post.content()); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /docs/ch19/ch19-04-advanced-functions-and-closures.md: -------------------------------------------------------------------------------- 1 | # 19.4 - Advanced Functions and Closures 2 | 3 | ## Function Pointers 4 | 5 | In [chapter 13][chap13] we saw you could pass a closure to a function, but we can also pass a function to a function! 6 | 7 | ```rust 8 | fn add_one(x: i32) -> i32 { 9 | x + 1 10 | } 11 | 12 | // Note that this takes a function pointer as 13 | // a parameter, and not a closure. 14 | fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { 15 | f(arg) + f(arg) 16 | } 17 | 18 | fn main() { 19 | let answer = do_twice(add_one, 5); 20 | 21 | println!("The answer is: {}", answer); 22 | } 23 | ``` 24 | 25 | The first parameter to `do_twice` is called a _function pointer_. You may recall [from chapter 13][chap13] that in order to pass a closure as a parameter, we declared a generic function and used a trait bound on the generic type to `FnOnce`, `FnMut`, or `Fn`. The difference between a closure and a function pointer is the function pointer is a named concrete type instead of a generic trait bound. (Technically any given closure has a concrete type as well, generated at compile time, but these are unnameable types.) 26 | 27 | Because a function is like a closure that cannot capture any variables, function pointers implement all three generic traits (`FnOnce`, `FnMut`, and `Fn`) so you can always pass a function pointer to a function that expects a trait. For this reason, it's generally more flexible to write a function that takes a closure. You'll likely have to use a function pointer instead If you're interacting with C code. 28 | 29 | ## Passing Functions In Place of a Closure 30 | 31 | Here's an example of using a function in place of a closure: 32 | 33 | ```rust 34 | let list_of_numbers = vec![1, 2, 3]; 35 | let list_of_strings: Vec = 36 | list_of_numbers.iter().map(|i| i.to_string()).collect(); 37 | 38 | // This is equivalent to the above: 39 | let list_of_strings2: Vec = 40 | list_of_numbers.iter().map(ToString::to_string).collect(); 41 | ``` 42 | 43 | Each enum variant we define becomes an initializer function, so we can use them as function pointers (as we also could any constructor): 44 | 45 | ```rust 46 | enum Status { 47 | Value(u32), 48 | Stop, 49 | } 50 | 51 | let list_of_statuses: Vec = (0u32..20).map(Status::Value).collect(); 52 | ``` 53 | 54 | ## Returning Closures 55 | 56 | Since a closure is defined using a trait, if you want to return one from a function you'll have to use a trait object: 57 | 58 | ```rust 59 | fn returns_closure() -> Box i32> { 60 | Box::new(|x| x + 1) 61 | } 62 | ``` 63 | 64 | [chap13]: ../ch13-functional-language-features.md "Chapter 13: Functional Language Features: Iterators and Closures" 65 | -------------------------------------------------------------------------------- /docs/ch01-getting-started.md: -------------------------------------------------------------------------------- 1 | # 1 - Getting Started 2 | 3 | This chapter is going to get Rust installed, and explain how to use `cargo` to create and build a new project. 4 | 5 | ## 1.1 - Installation 6 | 7 | On Linux or MacOS: 8 | 9 | ```sh 10 | curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh 11 | xcode-select --install 12 | ``` 13 | 14 | On Windows, you can still use `rustup`, but check [the official documentation](https://forge.rust-lang.org/infra/other-installation-methods.html) for how to go about installing it. 15 | 16 | Verify your installation with `rustc --version`. Upgrade with `rustup update`. 17 | 18 | ## 1.2 - Hello, World! 19 | 20 | Tradition dictates that we can't learn a new programming language without starting with a program that prints "Hello, world!". Let's create a file called "main.rs": 21 | 22 | ```rust title="main.rs" 23 | fn main() { 24 | println!("Hello, world!"); 25 | } 26 | ``` 27 | 28 | Indenting in Rust is done with four spaces, not tabs, and statements end with ";". Here we're calling the `println!` macro - you can tell its a macro because it ends with `!`. You can run this with: 29 | 30 | ```sh 31 | $ rustc main.rs 32 | $ ./main 33 | Hello, world! 34 | ``` 35 | 36 | `rustc` compiles the source to an executable called `main`, and then `./main` runs the executable. 37 | 38 | ## 1.3 - Hello, Cargo! 39 | 40 | Cargo is Rust's build system and package manager. It's a bit like `npm` in JavaScript, or the `go` command in Go. 41 | 42 | ### Creating a Project with Cargo 43 | 44 | ```sh 45 | $ cargo new hello_cargo 46 | $ cd hello_cargo 47 | ``` 48 | 49 | This creates a _Cargo.toml_ file and a _src/main.rs_ file inside a new folder, and also initializes the new folder as a git repo. _Cargo.toml_ is a [toml](https://toml.io/en/) file (which looks a lot like an old-fashioned Windows .ini file). The `[package]` section of _Cargo.toml_ describes metadata about the current package, and the `[dependencies]` section lists any libraries (aka _crates_) that your project depends on. 50 | 51 | We can build and run this project with: 52 | 53 | ```sh 54 | $ cargo build 55 | $ ./target/debug/hello_cargo 56 | ``` 57 | 58 | Or with this shortcut, which is equivalent to the above: 59 | 60 | ```sh 61 | $ cargo run 62 | ``` 63 | 64 | Note that `cargo build` creates a _Cargo.lock_ file - this is a dependency lockfile which means if you share this project with a friend, and they `cargo build`, they'll get exactly the same dependencies that you did. It's a good idea to commit the lockfile to your source code management tool. 65 | 66 | You can verify that a project compiles without producing an executable with `cargo check`, which is often much faster than `cargo build`. You can build a "release version" of your code with `cargo build --release` which will generate an executable in target/release instead of target/debug. The release version will be missing symbols and some runtime safety checks, and will be better optimized. 67 | 68 | Continue to [chapter 2][chap2]. 69 | 70 | [chap2]: ./ch02-guessing-game.md "Chapter 2: Guessing Game" 71 | -------------------------------------------------------------------------------- /docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | 4 | const lightCodeTheme = require("prism-react-renderer/themes/github"); 5 | const darkCodeTheme = require("prism-react-renderer/themes/dracula"); 6 | 7 | /** @type {import('@docusaurus/types').Config} */ 8 | const config = { 9 | title: "The rs Book", 10 | tagline: "The Rust Programming Language Abridged", 11 | favicon: "img/favicon.ico", 12 | 13 | // Set the production url of your site here 14 | url: "https://www.jasonwalton.ca", 15 | // Set the // pathname under which your site is served 16 | // For GitHub pages deployment, it is often '//' 17 | baseUrl: "/rust-book-abridged/", 18 | 19 | // GitHub pages deployment config. 20 | // If you aren't using GitHub pages, you don't need these. 21 | organizationName: "jwalton", 22 | projectName: "rust-book-abridged", 23 | 24 | onBrokenLinks: "throw", 25 | onBrokenMarkdownLinks: "warn", 26 | 27 | // Even if you don't use internalization, you can use this field to set useful 28 | // metadata like html lang. For example, if your site is Chinese, you may want 29 | // to replace "en" with "zh-Hans". 30 | i18n: { 31 | defaultLocale: "en", 32 | locales: ["en"], 33 | }, 34 | 35 | presets: [ 36 | [ 37 | "classic", 38 | /** @type {import('@docusaurus/preset-classic').Options} */ 39 | ({ 40 | docs: { 41 | routeBasePath: "/", 42 | sidebarPath: require.resolve("./sidebars.js"), 43 | // Please change this to your repo. 44 | // Remove this to remove the "edit this page" links. 45 | editUrl: 46 | "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/", 47 | }, 48 | blog: false, 49 | theme: { 50 | customCss: require.resolve("./src/css/custom.css"), 51 | }, 52 | }), 53 | ], 54 | ], 55 | 56 | themeConfig: 57 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 58 | ({ 59 | // Replace with your project's social card 60 | image: "img/social-card.jpg", 61 | navbar: { 62 | title: "The Rust Book (Abridged)", 63 | logo: { 64 | alt: "Logo", 65 | src: "img/logo.svg", 66 | }, 67 | items: [ 68 | { 69 | href: "https://thedreaming.org", 70 | label: "Blog", 71 | position: "left", 72 | }, 73 | { 74 | href: "https://github.com/jwalton/rust-book-abridged", 75 | label: "GitHub", 76 | position: "right", 77 | }, 78 | ], 79 | }, 80 | footer: { 81 | style: "dark", 82 | links: [], 83 | copyright: `Copyright © ${new Date().getFullYear()} Jason Walton, Inc. Built with Docusaurus.`, 84 | }, 85 | prism: { 86 | theme: lightCodeTheme, 87 | darkTheme: darkCodeTheme, 88 | additionalLanguages: ["rust"], 89 | }, 90 | }), 91 | }; 92 | 93 | module.exports = config; 94 | -------------------------------------------------------------------------------- /.github/workflows/deploy-gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - "*" 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | build: 15 | name: Build 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: actions/setup-node@v3 20 | with: 21 | node-version: 18 22 | cache: npm 23 | - name: Install dependencies 24 | run: npm ci 25 | - name: Build 26 | run: npm run build 27 | - name: Upload build artifacts 28 | uses: actions/upload-artifact@v3 29 | with: 30 | name: build-output 31 | path: build/ 32 | 33 | deploy: 34 | name: Deploy 35 | runs-on: ubuntu-latest 36 | needs: build 37 | # Only deploy non-tagged builds (otherwise we deploy twice when 38 | # we tag a release). 39 | if: "!startsWith(github.ref, 'refs/tags/')" 40 | permissions: 41 | contents: write 42 | steps: 43 | - name: Download build artifacts 44 | uses: actions/download-artifact@v3 45 | with: 46 | name: build-output 47 | path: build/ 48 | - name: Deploy to GitHub Pages 49 | uses: peaceiris/actions-gh-pages@v3 50 | if: ${{ github.ref == 'refs/heads/master' }} 51 | with: 52 | github_token: ${{ secrets.GITHUB_TOKEN }} 53 | # Build output to publish to the `gh-pages` branch: 54 | publish_dir: ./build 55 | user_name: jwalton 56 | user_email: dev@lucid.thedreaming.org 57 | 58 | pdf: 59 | name: Generate PDF Version 60 | runs-on: ubuntu-latest 61 | permissions: 62 | contents: write 63 | needs: build 64 | # Generate a new release every time there's a tag. 65 | if: startsWith(github.ref, 'refs/tags/') 66 | steps: 67 | - name: Download build artifacts 68 | uses: actions/download-artifact@v3 69 | with: 70 | name: build-output 71 | path: build/ 72 | - name: Install Prince (HTML to PDF converter) 73 | run: | 74 | curl https://www.princexml.com/download/prince-14.2-linux-generic-x86_64.tar.gz -O 75 | tar zxf prince-14.2-linux-generic-x86_64.tar.gz 76 | cd prince-14.2-linux-generic-x86_64 77 | yes "" | sudo ./install.sh 78 | - name: Start Server and Build PDF 79 | run: | 80 | # Symlink to make docusaurus `baseUrl` working 81 | ln -s ./build rust-book-abridged 82 | # Start local server and wait for it to be ready 83 | npx serve . -p 1337 & npx wait-on http://localhost:1337/rust-book-abridged 84 | # Generate PDF 85 | npx docusaurus-prince-pdf -u http://localhost:1337/rust-book-abridged --output rust-book-abridged.pdf 86 | # Kill server 87 | fuser -k 1337/tcp 88 | - uses: ncipollo/release-action@v1 89 | with: 90 | generateReleaseNotes: true 91 | makeLatest: true 92 | artifacts: "rust-book-abridged.pdf" 93 | -------------------------------------------------------------------------------- /examples/ch20-graceful-shutdown/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | sync::{mpsc, Arc, Mutex}, 3 | thread::{self, JoinHandle}, 4 | }; 5 | 6 | type Job = Box; 7 | 8 | pub struct ThreadPool { 9 | workers: Vec, 10 | sender: Option>, 11 | } 12 | 13 | impl ThreadPool { 14 | /// Create a new ThreadPool. 15 | /// 16 | /// The size is the number of threads in the pool. 17 | /// 18 | /// # Panics 19 | /// 20 | /// The `new` function will panic if the size is zero. 21 | pub fn new(size: usize) -> ThreadPool { 22 | // Make sure `size` is valid. 23 | assert!(size > 0); 24 | 25 | // Create our sender and receiver 26 | let (sender, receiver) = mpsc::channel(); 27 | let receiver = Arc::new(Mutex::new(receiver)); 28 | 29 | // Create a new vector. Pre-allocate the vector 30 | // to be of length `size` so we know it can store 31 | // all of our threads. 32 | let mut workers = Vec::with_capacity(size); 33 | 34 | // Create new workers and add them to the pool. 35 | for id in 0..size { 36 | workers.push(Worker::new(id, Arc::clone(&receiver))); 37 | } 38 | 39 | ThreadPool { 40 | workers, 41 | sender: Some(sender), 42 | } 43 | } 44 | 45 | pub fn execute(&self, f: F) 46 | where 47 | F: FnOnce() + Send + 'static, 48 | { 49 | // Send our job to a Worker. 50 | let job = Box::new(f); 51 | self.sender.as_ref().unwrap().send(job).unwrap(); 52 | } 53 | } 54 | 55 | impl Drop for ThreadPool { 56 | fn drop(&mut self) { 57 | // Drop the sender to force all the workers to finish up. 58 | drop(self.sender.take()); 59 | 60 | for worker in &mut self.workers { 61 | println!("Shutting down worker {}", worker.id); 62 | 63 | // If there's a thread in this worker, wait for 64 | // it to finish. If thread is None, there's 65 | // nothing to clean up. 66 | if let Some(thread) = worker.thread.take() { 67 | thread.join().unwrap(); 68 | } 69 | } 70 | } 71 | } 72 | 73 | struct Worker { 74 | id: usize, 75 | thread: Option>, 76 | } 77 | 78 | impl Worker { 79 | fn new(id: usize, receiver: Arc>>) -> Worker { 80 | let thread = thread::spawn(move || loop { 81 | let message = receiver.lock().unwrap().recv(); 82 | 83 | match message { 84 | Ok(job) => { 85 | println!("Worker {id} got a job; executing."); 86 | job(); 87 | } 88 | Err(_) => { 89 | println!("Worker {id} disconnected; shutting down."); 90 | break; 91 | } 92 | } 93 | }); 94 | 95 | Worker { 96 | id, 97 | thread: Some(thread), 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /docs/zz-appendix/appendix-05-editions.md: -------------------------------------------------------------------------------- 1 | # Appendix E - Editions 2 | 3 | This appendix was copied directly from ["The Rust Programming Language"](https://doc.rust-lang.org/stable/book/appendix-05-editions.html). 4 | 5 | In Chapter 1, you saw that `cargo new` adds a bit of metadata to your 6 | _Cargo.toml_ file about an edition. This appendix talks about what that means! 7 | 8 | The Rust language and compiler have a six-week release cycle, meaning users get 9 | a constant stream of new features. Other programming languages release larger 10 | changes less often; Rust releases smaller updates more frequently. After a 11 | while, all of these tiny changes add up. But from release to release, it can be 12 | difficult to look back and say, "Wow, between Rust 1.10 and Rust 1.31, Rust has 13 | changed a lot!" 14 | 15 | Every two or three years, the Rust team produces a new Rust _edition_. Each 16 | edition brings together the features that have landed into a clear package with 17 | fully updated documentation and tooling. New editions ship as part of the usual 18 | six-week release process. 19 | 20 | Editions serve different purposes for different people: 21 | 22 | - For active Rust users, a new edition brings together incremental changes into 23 | an easy-to-understand package. 24 | - For non-users, a new edition signals that some major advancements have 25 | landed, which might make Rust worth another look. 26 | - For those developing Rust, a new edition provides a rallying point for the 27 | project as a whole. 28 | 29 | At the time of this writing, three Rust editions are available: Rust 2015, Rust 30 | 2018, and Rust 2021. This book is written using Rust 2021 edition idioms. 31 | 32 | The `edition` key in _Cargo.toml_ indicates which edition the compiler should 33 | use for your code. If the key doesn't exist, Rust uses `2015` as the edition 34 | value for backward compatibility reasons. 35 | 36 | Each project can opt in to an edition other than the default 2015 edition. 37 | Editions can contain incompatible changes, such as including a new keyword that 38 | conflicts with identifiers in code. However, unless you opt in to those 39 | changes, your code will continue to compile even as you upgrade the Rust 40 | compiler version you use. 41 | 42 | All Rust compiler versions support any edition that existed prior to that 43 | compiler's release, and they can link crates of any supported editions 44 | together. Edition changes only affect the way the compiler initially parses 45 | code. Therefore, if you're using Rust 2015 and one of your dependencies uses 46 | Rust 2018, your project will compile and be able to use that dependency. The 47 | opposite situation, where your project uses Rust 2018 and a dependency uses 48 | Rust 2015, works as well. 49 | 50 | To be clear: most features will be available on all editions. Developers using 51 | any Rust edition will continue to see improvements as new stable releases are 52 | made. However, in some cases, mainly when new keywords are added, some new 53 | features might only be available in later editions. You will need to switch 54 | editions if you want to take advantage of such features. 55 | 56 | For more details, the [_Edition 57 | Guide_](https://doc.rust-lang.org/stable/edition-guide/) is a complete book 58 | about editions that enumerates the differences between editions and explains 59 | how to automatically upgrade your code to a new edition via `cargo fix`. 60 | -------------------------------------------------------------------------------- /docs/zz-appendix/appendix-01-keywords.md: -------------------------------------------------------------------------------- 1 | # Appendix A: Keywords 2 | 3 | This appendix was copied directly from ["The Rust Programming Language"](https://doc.rust-lang.org/stable/book/appendix-01-keywords.html). 4 | 5 | The following list contains keywords that are reserved for current or future 6 | use by the Rust language. As such, they cannot be used as identifiers (except 7 | as raw identifiers as we’ll discuss in the “[Raw 8 | Identifiers][raw-identifiers]” section). Identifiers are names 9 | of functions, variables, parameters, struct fields, modules, crates, constants, 10 | macros, static values, attributes, types, traits, or lifetimes. 11 | 12 | [raw-identifiers]: #raw-identifiers 13 | 14 | ## Keywords Currently in Use 15 | 16 | The following is a list of keywords currently in use, with their functionality 17 | described. 18 | 19 | * `as` - perform primitive casting, disambiguate the specific trait containing 20 | an item, or rename items in `use` statements 21 | * `async` - return a `Future` instead of blocking the current thread 22 | * `await` - suspend execution until the result of a `Future` is ready 23 | * `break` - exit a loop immediately 24 | * `const` - define constant items or constant raw pointers 25 | * `continue` - continue to the next loop iteration 26 | * `crate` - in a module path, refers to the crate root 27 | * `dyn` - dynamic dispatch to a trait object 28 | * `else` - fallback for `if` and `if let` control flow constructs 29 | * `enum` - define an enumeration 30 | * `extern` - link an external function or variable 31 | * `false` - Boolean false literal 32 | * `fn` - define a function or the function pointer type 33 | * `for` - loop over items from an iterator, implement a trait, or specify a 34 | higher-ranked lifetime 35 | * `if` - branch based on the result of a conditional expression 36 | * `impl` - implement inherent or trait functionality 37 | * `in` - part of `for` loop syntax 38 | * `let` - bind a variable 39 | * `loop` - loop unconditionally 40 | * `match` - match a value to patterns 41 | * `mod` - define a module 42 | * `move` - make a closure take ownership of all its captures 43 | * `mut` - denote mutability in references, raw pointers, or pattern bindings 44 | * `pub` - denote public visibility in struct fields, `impl` blocks, or modules 45 | * `ref` - bind by reference 46 | * `return` - return from function 47 | * `Self` - a type alias for the type we are defining or implementing 48 | * `self` - method subject or current module 49 | * `static` - global variable or lifetime lasting the entire program execution 50 | * `struct` - define a structure 51 | * `super` - parent module of the current module 52 | * `trait` - define a trait 53 | * `true` - Boolean true literal 54 | * `type` - define a type alias or associated type 55 | * `union` - define a [union][union]; is only a keyword when used 56 | in a union declaration 57 | * `unsafe` - denote unsafe code, functions, traits, or implementations 58 | * `use` - bring symbols into scope 59 | * `where` - denote clauses that constrain a type 60 | * `while` - loop conditionally based on the result of an expression 61 | 62 | [union]: https://doc.rust-lang.org/stable/reference/items/unions.html 63 | 64 | ## Keywords Reserved for Future Use 65 | 66 | The following keywords do not yet have any functionality but are reserved by 67 | Rust for potential future use. 68 | 69 | * `abstract` 70 | * `become` 71 | * `box` 72 | * `do` 73 | * `final` 74 | * `macro` 75 | * `override` 76 | * `priv` 77 | * `try` 78 | * `typeof` 79 | * `unsized` 80 | * `virtual` 81 | * `yield` 82 | 83 | ### Raw Identifiers 84 | 85 | *Raw identifiers* are the syntax that lets you use keywords where they wouldn’t 86 | normally be allowed. You use a raw identifier by prefixing a keyword with `r#`. 87 | 88 | For example, `match` is a keyword. If you try to compile the following function 89 | that uses `match` as its name: 90 | 91 | ```rust title="src/main.rs" 92 | fn match(needle: &str, haystack: &str) -> bool { 93 | haystack.contains(needle) 94 | } 95 | ``` 96 | 97 | you'll get this error: 98 | 99 | ```text 100 | error: expected identifier, found keyword `match` 101 | --> src/main.rs:4:4 102 | | 103 | 4 | fn match(needle: &str, haystack: &str) -> bool { 104 | | ^^^^^ expected identifier, found keyword 105 | ``` 106 | 107 | The error shows that you can’t use the keyword `match` as the function 108 | identifier. To use `match` as a function name, you need to use the raw 109 | identifier syntax, like this: 110 | 111 | ```rust title="src/main.rs" 112 | fn r#match(needle: &str, haystack: &str) -> bool { 113 | haystack.contains(needle) 114 | } 115 | 116 | fn main() { 117 | assert!(r#match("foo", "foobar")); 118 | } 119 | ``` 120 | 121 | This code will compile without any errors. Note the `r#` prefix on the function 122 | name in its definition as well as where the function is called in `main`. 123 | 124 | Raw identifiers allow you to use any word you choose as an identifier, even if 125 | that word happens to be a reserved keyword. This gives us more freedom to 126 | choose identifier names, as well as lets us integrate with programs written in 127 | a language where these words aren’t keywords. In addition, raw identifiers 128 | allow you to use libraries written in a different Rust edition than your crate 129 | uses. For example, `try` isn’t a keyword in the 2015 edition but is in the 2018 130 | edition. If you depend on a library that’s written using the 2015 edition and 131 | has a `try` function, you’ll need to use the raw identifier syntax, `r#try` in 132 | this case, to call that function from your 2018 edition code. See [Appendix 133 | E][appendix-e] for more information on editions. 134 | 135 | [appendix-e]: ./appendix-05-editions.md 136 | -------------------------------------------------------------------------------- /docs/ch19/ch19-03-advanced-types.md: -------------------------------------------------------------------------------- 1 | # 19.3 - Advanced Types 2 | 3 | ## Using the Newtype Pattern for Type Safety and Abstraction 4 | 5 | In the [previous section](./ch19-02-advanced-traits.md#using-the-newtype-pattern-to-implement-external-traits-on-external-types), we discussed using the _newtype_ pattern to wrap an existing type in a tuple. 6 | 7 | The newtype pattern is useful in a few other scenarios too. If we create a `Millisecond` type: 8 | 9 | ```rust 10 | struct Millisecond(u32); 11 | 12 | fn sleep(duration: Millisecond) { 13 | // --snip-- 14 | } 15 | ``` 16 | 17 | This makes it very clear that `sleep` expects a value in milliseconds (although in this particular example you'd be better off using [`std::time::Duration`](https://doc.rust-lang.org/std/time/struct.Duration.html). The newtype pattern can also be used to wrap a type and give it a different public API. 18 | 19 | ## Creating Type Synonyms with Type Aliases 20 | 21 | We can give an existing type a _type alias_: 22 | 23 | ```rust 24 | type Kilometers = i32; 25 | 26 | let x: i32 = 5; 27 | let y: Kilometers = 5; 28 | 29 | println!("x + y = {}", x + y); 30 | ``` 31 | 32 | This creates a new type called `Kilometers` which is an alias for i32. You can now use these types interchangeably - if a function expects a `Kilometers` you can pass in an i32 instead or vice versa. 33 | 34 | The main use case for type aliases is to reduce the length of long type names. We can take code like this: 35 | 36 | ```rust 37 | let f: Box = Box::new(|| println!("hi")); 38 | 39 | fn takes_long_type(f: Box) { 40 | // --snip-- 41 | } 42 | 43 | fn returns_long_type() -> Box { 44 | // --snip-- 45 | } 46 | ``` 47 | 48 | and turn it into: 49 | 50 | ```rust 51 | type Thunk = Box; 52 | 53 | let f: Thunk = Box::new(|| println!("hi")); 54 | 55 | fn takes_long_type(f: Thunk) { 56 | // --snip-- 57 | } 58 | 59 | fn returns_long_type() -> Thunk { 60 | // --snip-- 61 | } 62 | ``` 63 | 64 | A meaningful name for your alias can make your code much easier to read and write. Another example of this is in `std::io`. Many functions here return a `Result` with a `std::io::Error` as the error type, so `std:io` defines: 65 | 66 | ```rust 67 | type Result = std::result::Result; 68 | ``` 69 | 70 | which makes a lot of function signatures in this module much shorter and easier to read. 71 | 72 | ## The Never Type that Never Returns 73 | 74 | There's a special type named `!`. This is the _empty type_ or _never type_: 75 | 76 | ```rust 77 | fn bar() -> ! { 78 | // --snip-- 79 | } 80 | ``` 81 | 82 | Here this tells the compiler that the `bar` function will never return. Way back in [chapter 2][chap2] we wrote this code: 83 | 84 | ```rust 85 | loop { 86 | // --snip-- 87 | let guess: u32 = match guess.trim().parse() { 88 | Ok(num) => num, 89 | Err(_) => continue, 90 | }; 91 | // --snip-- 92 | } 93 | ``` 94 | 95 | The arms of a `match` are supposed to all be of the same type in order for it to compile. You can't have one arm evaluate to a `u32` and another evaluate to a `String`. Here though, we know that the `Err(_)` arm isn't going to return anything - if we get here, we'll abort this run through the loop and continue. From a type perspective, the return value of `continue` is `!`, so here Rust knows it's safe to ignore this arm (or to put it another way, the `!` type can be coerced to any other type). 96 | 97 | The `panic!` macro is another example of something that evaluates to the `!` type: 98 | 99 | ```rust 100 | impl Option { 101 | pub fn unwrap(self) -> T { 102 | match self { 103 | Some(val) => val, 104 | None => panic!("called `Option::unwrap()` on a `None` value"), 105 | } 106 | } 107 | } 108 | ``` 109 | 110 | A `loop` without a `break` is also of type `!`. 111 | 112 | ## Dynamically Sized Types and the Sized Trait 113 | 114 | When we create a variable on the stack, Rust needs to know how much space to allocate for that variable at compile time. For example: 115 | 116 | ```rust 117 | fn add(a: i32, b: i32) { 118 | println!("{}", a + b); 119 | } 120 | ``` 121 | 122 | When someone calls `add`, Rust will need to allocate four bytes on the stack to hold `a`, and another four to hold `b`. 123 | 124 | Consider a string, which holds a variable amount of data: 125 | 126 | ```rust 127 | fn say_hello(name: &str) { 128 | println!("Hello {name}"); 129 | } 130 | ``` 131 | 132 | Here `name` isn't actually a `str` but a `&str` or a string slice. The actual data from the string is stored "somewhere else" in memory, but the `name` variable itself is 16 bytes long on a 64-bit platform (two `usize`s). This is because `&str` is implemented as a pointer to the string data and a length value. 133 | 134 | As a rule, to pass around _dynamically sized types_ like a string, we need a pointer. This can be a `Box` or an `Rc` or a `&`, but some kind of pointer. Another example of a dynamically sized type is a trait object, which is why when we pass one it's usually in a `Box`. The size of the trait object itself is unknown, so we pass around a smart pointer to the trait object instead, allowing us to store the trait object on the heap. 135 | 136 | Any type whose size is known at compile time automatically implements the `Sized` trait. Generic functions implicitly get a trait bounds for `Sized`: 137 | 138 | ```rust 139 | // You write this: 140 | fn my_generic_fn(t: T) { 141 | // --snip-- 142 | } 143 | 144 | // But Rust implicitly turns this into: 145 | fn my_generic_fn(t: T) { 146 | // --snip-- 147 | } 148 | ``` 149 | 150 | You can prevent this with this syntax: 151 | 152 | ```rust 153 | // T doesn't have to be `Sized`. 154 | fn generic(t: &T) { 155 | // --snip-- 156 | } 157 | ``` 158 | 159 | Note that in order to do this, we can't leave the `t` parameter of type `T`. We again need some kind of pointer, in this case we chose `&T`. 160 | 161 | [chap2]: ../ch02-guessing-game.md "Chapter 2: Guessing Game" 162 | -------------------------------------------------------------------------------- /docs/ch20/ch20-01-single-threaded-web-server.md: -------------------------------------------------------------------------------- 1 | # 20.1 - Building a Single-Threaded Web Server 2 | 3 | In this chapter we're going to build a simple HTTP server to put together a number of things we've learned so far. As usual, the code for this project is [available in the GitHub repo](https://github.com/jwalton/rust-book-abridged/tree/master/examples/ch20-single-threaded-web-server). 4 | 5 | ## HTTP Requests 6 | 7 | An HTTP GET request looks something like: 8 | 9 | ```txt 10 | GET /index.html HTTP/1.1 11 | Host: example.com 12 | Accept-Language: en-us 13 | Accept-Encoding: gzip, deflate 14 | Connection: Keep-Alive 15 | 16 | ``` 17 | 18 | Each newline here is actually a CRLF or a `\r\n`. The first line is of the format `Method Request-URI HTTP-Version CRLF`. This is followed by one or more _headers_, followed by a blank line, and then optionally a body. (For our server, we'll assume only a maniac would send a GET with a body.) 19 | 20 | The response looks very similar: 21 | 22 | ```txt 23 | HTTP/1.1 200 OK 24 | Content-Type: text 25 | Content-Length: 26 26 | 27 | Hello, World! 28 | ``` 29 | 30 | The first line is `HTTP-Version Status-Code Reason-Phrase CRLF`, and this is followed by headers, a blank line, and then the response body. 31 | 32 | ## Some HTML to Serve 33 | 34 | Let's create a project: 35 | 36 | ```sh 37 | $ cargo new hello 38 | $ cd hello 39 | ``` 40 | 41 | In order to create a server, first we need something to serve, so we'll create a couple of HTML file: 42 | 43 | ```html title="hello.html" 44 | 45 | 46 | 47 | 48 | Hello! 49 | 50 | 51 |

Hello!

52 |

Hi from Rust

53 | 54 | 55 | ``` 56 | 57 | and: 58 | 59 | ```html title="404.html" 60 | 61 | 62 | 63 | 64 | Hello! 65 | 66 | 67 |

Oops!

68 |

Sorry, I don't know what you're asking for.

69 | 70 | 71 | ``` 72 | 73 | And then here is the code for our server: 74 | 75 | ```rust title="src/main.rs" 76 | use std::{ 77 | fs, 78 | io::{prelude::*, BufReader}, 79 | net::{TcpListener, TcpStream}, 80 | }; 81 | 82 | fn main() { 83 | let port = 7878u16; 84 | let listen_address = format!("127.0.0.1:{port}"); 85 | let listener = TcpListener::bind(listen_address).unwrap(); 86 | 87 | println!("Listening on port {}", port); 88 | 89 | for stream in listener.incoming() { 90 | let stream = stream.unwrap(); 91 | 92 | handle_connection(stream); 93 | } 94 | } 95 | 96 | fn handle_connection(mut stream: TcpStream) { 97 | let buf_reader = BufReader::new(&mut stream); 98 | 99 | // A line could be an error if it contains invalid 100 | // UTF-8, or if there's a problem reading from the 101 | // underlying stream. We ignore these errors here. 102 | let http_request: Vec<_> = buf_reader 103 | .lines() 104 | .map(|result| result.unwrap()) 105 | .take_while(|line| !line.is_empty()) // Blank line is end of headers. 106 | .collect(); 107 | 108 | let request_line = &http_request[0]; 109 | 110 | println!("Incoming request for {}", request_line); 111 | 112 | if request_line == "GET / HTTP/1.1" { 113 | send_response(stream, 200, "OK", "hello.html"); 114 | } else { 115 | send_response(stream, 404, "NOT FOUND", "404.html"); 116 | } 117 | } 118 | 119 | fn send_response(mut stream: TcpStream, code: u16, reason: &str, filename: &str) { 120 | let contents = fs::read_to_string(filename).unwrap(); 121 | let length = contents.len(); 122 | let response = 123 | format!("HTTP/1.1 {code} {reason}\r\nContent-Length: {length}\r\n\r\n{contents}"); 124 | 125 | stream.write_all(response.as_bytes()).unwrap(); 126 | } 127 | ``` 128 | 129 | If we `cargo run` this and point a browser at [http://localhost:7878/](http://localhost:7878/), we should see our web page! 130 | 131 | ## Listening to the TCP Connection 132 | 133 | Let's start with the `main` function. We call `TcpListener::bind` to start listening on a port. This returns a `TcpListener` instance, so it's basically a constructor for `TcpListener`. Note that we're binding to "127.0.0.1", so you'll only be able to access this web server from the same machine you're running it on. We could bind to "0.0.0.0" - the [unspecified address](https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html#method.is_unspecified) - to bind to all local interfaces. `bind` can fail for a variety of reasons. For example, if we tried to bind to port 80 and we weren't root, this would fail because we don't have sufficient permissions, or some other process might have already bound the port. We're glossing over all the error handling with a call to `unwrap`. 134 | 135 | Once we have out `TcpListener` we call `incoming` on it, which returns an iterator of `Result`. We'll get an item from this iterator every time a client tries to connect. Note this iterator will never return `None`! This loop is going to go on forever (or at least until we hit CTRL-C to terminate this program). A connection attempt can fail for a variety of reasons. In a production web server we'd want to handle these, but here we're once again just calling `unwrap`. Finally we hand of the connection to `handle_connection`. 136 | 137 | ## Parsing the Request 138 | 139 | Our `handle_connection` function creates a new buffered reader to read the incoming bytes from the stream. We user our reader to read in the request, split it into lines, then collect lines into a vector until we reach an empty line. As we've seen before, calling `collect` requires us to annotate the type of `http_request` so `collect` will know what kind of collection to return. 140 | 141 | Once we have our request, we call into `send_response` to generate an appropriate response back to the client. 142 | 143 | And that's all there is too it! Our server only runs in a single thread, so it can only handle a single request at a time. In the next section, we'll upgrade this server to run [in multiple threads](./ch20-02-multi-threaded-web-server.md). 144 | -------------------------------------------------------------------------------- /docs/ch20/ch20-03-graceful-shutdown.md: -------------------------------------------------------------------------------- 1 | # 20.3 - Graceful Shutdown and Cleanup 2 | 3 | Right now when we hit CTRL-C to send an interrupt signal to our web server, it stops running, but it also stops any in-flight requests. Let's see if we can get our server to shut down gracefully. 4 | 5 | The basic strategy here is going to be to implement the `Drop` trait on `ThreadPool`. When the `ThreadPool` is dropped, we'll signal all the threads that they should stop accepting new requests and quit, and then we'll call `join` on each one to give them the time they need to finish up. 6 | 7 | If you're looking for the full source for this project, it's [in the GitHub repo](https://github.com/jwalton/rust-book-abridged/tree/master/examples/ch20-graceful-shutdown) 8 | 9 | ## Implementing the `Drop` Trait on `ThreadPool` 10 | 11 | One problem we're going to run into is that, in order to call `thread.join()`, we're going to have to move the `thread` out of the `Worker`. We can't move _part_ of a struct, so we're going to have to use the same trick we did in [chapter 17][chap17] and store the thread in an `Option` so we can set it to `None`. 12 | 13 | Calling `join` isn't enough though. This will wait until each thread quits, but right now the closure in each thread is an infinite loop! We need to somehow signal to the `Worker`'s thread that it should stop accepting new jobs. We can do this by dropping the `sender` half of the channel. This will cause the receiver to wake up and return an error. We'll have to pull the same trick we did with `thread` and store the `sender` in an `Option` to make this work, otherwise there's no way for us to drop the sender. We'll also want to handle the error from `recv` correctly instead of just panicking. 14 | 15 | Here's the updated library: 16 | 17 | ```rust title="src/lib.rs" 18 | use std::{ 19 | sync::{mpsc, Arc, Mutex}, 20 | thread::{self, JoinHandle}, 21 | }; 22 | 23 | type Job = Box; 24 | 25 | pub struct ThreadPool { 26 | workers: Vec, 27 | sender: Option>, 28 | } 29 | 30 | impl ThreadPool { 31 | /// Create a new ThreadPool. 32 | /// 33 | /// The size is the number of threads in the pool. 34 | /// 35 | /// # Panics 36 | /// 37 | /// The `new` function will panic if the size is zero. 38 | pub fn new(size: usize) -> ThreadPool { 39 | // Make sure `size` is valid. 40 | assert!(size > 0); 41 | 42 | // Create our sender and receiver 43 | let (sender, receiver) = mpsc::channel(); 44 | let receiver = Arc::new(Mutex::new(receiver)); 45 | 46 | // Create a new vector. Pre-allocate the vector 47 | // to be of length `size` so we know it can store 48 | // all of our threads. 49 | let mut workers = Vec::with_capacity(size); 50 | 51 | for id in 0..size { 52 | workers.push(Worker::new(id, Arc::clone(&receiver))); 53 | } 54 | 55 | ThreadPool { 56 | workers, 57 | sender: Some(sender), 58 | } 59 | } 60 | 61 | pub fn execute(&self, f: F) 62 | where 63 | F: FnOnce() + Send + 'static, 64 | { 65 | // Send our job to a Worker. 66 | let job = Box::new(f); 67 | self.sender.as_ref().unwrap().send(job).unwrap(); 68 | } 69 | } 70 | 71 | impl Drop for ThreadPool { 72 | fn drop(&mut self) { 73 | // Drop the sender to force all the workers to finish up. 74 | drop(self.sender.take()); 75 | 76 | for worker in &mut self.workers { 77 | println!("Shutting down worker {}", worker.id); 78 | 79 | // If there's a thread in this worker, wait for 80 | // it to finish. If thread is None, there's 81 | // nothing to clean up. 82 | if let Some(thread) = worker.thread.take() { 83 | thread.join().unwrap(); 84 | } 85 | } 86 | } 87 | } 88 | 89 | struct Worker { 90 | id: usize, 91 | thread: Option>, 92 | } 93 | 94 | impl Worker { 95 | fn new(id: usize, receiver: Arc>>) -> Worker { 96 | let thread = thread::spawn(move || loop { 97 | let message = receiver.lock().unwrap().recv(); 98 | 99 | match message { 100 | Ok(job) => { 101 | println!("Worker {id} got a job; executing."); 102 | job(); 103 | } 104 | Err(_) => { 105 | println!("Worker {id} disconnected; shutting down."); 106 | break; 107 | } 108 | } 109 | }); 110 | 111 | Worker { 112 | id, 113 | thread: Some(thread), 114 | } 115 | } 116 | } 117 | ``` 118 | 119 | Now we just need some way to make the server shut down. A simple way to do this for testing is to modify `main`: 120 | 121 | ```rust title="src/main.rs" 122 | // --snip-- 123 | for stream in listener.incoming().take(2) { 124 | // --snip-- 125 | ``` 126 | 127 | Now our server will shut down after two requests. Not exactly something we'd want to do in production, but it will prove our shutdown code is working here. 128 | 129 | ## Next Steps 130 | 131 | The original Rust book has some suggestions about places you could take this project further: 132 | 133 | > - Add more documentation to ThreadPool and its public methods. 134 | > - Add tests of the library's functionality. 135 | > - Change calls to unwrap to more robust error handling. 136 | > - Use ThreadPool to perform some task other than serving web requests. 137 | > - Find a thread pool crate on crates.io and implement a similar web server using the crate instead. Then compare its API and robustness to the thread pool we implemented. 138 | 139 | Another fun one might be to try to hook the SIGINT and SIGTERM signals so a CTRL-C will cause the server to shut down gracefully. 140 | 141 | This is as far as the original Rust book went, but you can continue on to our [special bonus chapter][chap21] to find out how we can rewrite this web server using async Rust! 142 | 143 | [chap17]: ../ch17-object-oriented-features.md "Chapter 17: Object Oriented Features of Rust" 144 | [chap21]: ../ch21-async.md "Chapter 21: Bonus Chapter: Async Programming" 145 | -------------------------------------------------------------------------------- /docs/zz-appendix/appendix-04-useful-development-tools.md: -------------------------------------------------------------------------------- 1 | # Appendix D - Useful Development Tools 2 | 3 | This appendix was copied directly from ["The Rust Programming Language"](https://doc.rust-lang.org/stable/book/appendix-04-useful-development-tools.html). 4 | 5 | In this appendix, we talk about some useful development tools that the Rust 6 | project provides. We'll look at automatic formatting, quick ways to apply 7 | warning fixes, a linter, and integrating with IDEs. 8 | 9 | ## Automatic Formatting with `rustfmt` 10 | 11 | The `rustfmt` tool reformats your code according to the community code style. 12 | Many collaborative projects use `rustfmt` to prevent arguments about which 13 | style to use when writing Rust: everyone formats their code using the tool. 14 | 15 | To install `rustfmt`, enter the following: 16 | 17 | ```console 18 | $ rustup component add rustfmt 19 | ``` 20 | 21 | This command gives you `rustfmt` and `cargo-fmt`, similar to how Rust gives you 22 | both `rustc` and `cargo`. To format any Cargo project, enter the following: 23 | 24 | ```console 25 | $ cargo fmt 26 | ``` 27 | 28 | Running this command reformats all the Rust code in the current crate. This 29 | should only change the code style, not the code semantics. For more information 30 | on `rustfmt`, see [its documentation][rustfmt]. 31 | 32 | [rustfmt]: https://github.com/rust-lang/rustfmt 33 | 34 | ## Fix Your Code with `rustfix` 35 | 36 | The rustfix tool is included with Rust installations and can automatically fix 37 | compiler warnings that have a clear way to correct the problem that's likely 38 | what you want. It's likely you've seen compiler warnings before. For example, 39 | consider this code: 40 | 41 | Filename: src/main.rs 42 | 43 | ```rust 44 | fn do_something() {} 45 | 46 | fn main() { 47 | for i in 0..100 { 48 | do_something(); 49 | } 50 | } 51 | ``` 52 | 53 | Here, we're calling the `do_something` function 100 times, but we never use the 54 | variable `i` in the body of the `for` loop. Rust warns us about that: 55 | 56 | ```console 57 | $ cargo build 58 | Compiling myprogram v0.1.0 (file:///projects/myprogram) 59 | warning: unused variable: `i` 60 | --> src/main.rs:4:9 61 | | 62 | 4 | for i in 0..100 { 63 | | ^ help: consider using `_i` instead 64 | | 65 | = note: #[warn(unused_variables)] on by default 66 | 67 | Finished dev [unoptimized + debuginfo] target(s) in 0.50s 68 | ``` 69 | 70 | The warning suggests that we use `_i` as a name instead: the underscore 71 | indicates that we intend for this variable to be unused. We can automatically 72 | apply that suggestion using the `rustfix` tool by running the command `cargo 73 | fix`: 74 | 75 | ```console 76 | $ cargo fix 77 | Checking myprogram v0.1.0 (file:///projects/myprogram) 78 | Fixing src/main.rs (1 fix) 79 | Finished dev [unoptimized + debuginfo] target(s) in 0.59s 80 | ``` 81 | 82 | When we look at *src/main.rs* again, we'll see that `cargo fix` has changed the 83 | code: 84 | 85 | ```rust title="src/main.rs" 86 | fn do_something() {} 87 | 88 | fn main() { 89 | for _i in 0..100 { 90 | do_something(); 91 | } 92 | } 93 | ``` 94 | 95 | The `for` loop variable is now named `_i`, and the warning no longer appears. 96 | 97 | You can also use the `cargo fix` command to transition your code between 98 | different Rust editions. Editions are covered in Appendix E. 99 | 100 | ## More Lints with Clippy 101 | 102 | The Clippy tool is a collection of lints to analyze your code so you can catch 103 | common mistakes and improve your Rust code. 104 | 105 | To install Clippy, enter the following: 106 | 107 | ```console 108 | $ rustup component add clippy 109 | ``` 110 | 111 | To run Clippy's lints on any Cargo project, enter the following: 112 | 113 | ```console 114 | $ cargo clippy 115 | ``` 116 | 117 | For example, say you write a program that uses an approximation of a 118 | mathematical constant, such as pi, as this program does: 119 | 120 | ```rust title="src/main.rs" 121 | fn main() { 122 | let x = 3.1415; 123 | let r = 8.0; 124 | println!("the area of the circle is {}", x * r * r); 125 | } 126 | ``` 127 | 128 | Running `cargo clippy` on this project results in this error: 129 | 130 | ```text 131 | error: approximate value of `f{32, 64}::consts::PI` found 132 | --> src/main.rs:2:13 133 | | 134 | 2 | let x = 3.1415; 135 | | ^^^^^^ 136 | | 137 | = note: `#[deny(clippy::approx_constant)]` on by default 138 | = help: consider using the constant directly 139 | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant 140 | ``` 141 | 142 | This error lets you know that Rust already has a more precise `PI` constant 143 | defined, and that your program would be more correct if you used the constant 144 | instead. You would then change your code to use the `PI` constant. The 145 | following code doesn't result in any errors or warnings from Clippy: 146 | 147 | ```rust title="src/main.rs" 148 | fn main() { 149 | let x = std::f64::consts::PI; 150 | let r = 8.0; 151 | println!("the area of the circle is {}", x * r * r); 152 | } 153 | ``` 154 | 155 | For more information on Clippy, see [its documentation][clippy]. 156 | 157 | [clippy]: https://github.com/rust-lang/rust-clippy 158 | 159 | ## IDE Integration Using `rust-analyzer` 160 | 161 | To help IDE integration, the Rust community recommends using 162 | [`rust-analyzer`][rust-analyzer]. This tool is a set of 163 | compiler-centric utilities that speaks the [Language Server Protocol][lsp], which is a specification for IDEs and programming languages to 165 | communicate with each other. Different clients can use `rust-analyzer`, such as 166 | [the Rust analyzer plug-in for Visual Studio Code][vscode]. 167 | 168 | [lsp]: http://langserver.org/ 169 | [vscode]: https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer 170 | 171 | Visit the `rust-analyzer` project's [home page][rust-analyzer] 172 | for installation instructions, then install the language server support in your 173 | particular IDE. Your IDE will gain abilities such as autocompletion, jump to 174 | definition, and inline errors. 175 | 176 | [rust-analyzer]: https://rust-analyzer.github.io 177 | -------------------------------------------------------------------------------- /docs/ch10/ch10-01-generic-data-types.md: -------------------------------------------------------------------------------- 1 | # 10.1 - Generic Data Types 2 | 3 | ## In Function Definitions 4 | 5 | We can use generics to define a function that can accept different data types. This is similar to generics in TypeScript, Java, and Go, and just like template functions in C++. 6 | 7 | Here's an example of a function to find the largest number in a list: 8 | 9 | ```rust 10 | fn largest_i32(list: &[i32]) -> &i32 { 11 | let mut largest = &list[0]; 12 | 13 | for item in list { 14 | if item > largest { 15 | largest = item; 16 | } 17 | } 18 | 19 | largest 20 | } 21 | 22 | 23 | fn main() { 24 | let number_list = vec![34, 50, 25, 100, 65]; 25 | 26 | let result = largest_i32(&number_list); 27 | println!("The largest number is {}", result); 28 | } 29 | ``` 30 | 31 | The problem with this function is that it can only accept a list of `i32`. If we wanted to write a version of this for `char` or for `u64`, the function signature would change, but the code in body would be identical. We can use generics here to write the function to accept any type by changing the function signature to: 32 | 33 | ```rust 34 | // This doesn't QUITE work... 35 | fn largest(list: &[T]) -> &T { 36 | ``` 37 | 38 | The `` after the function name tells the compiler this is a generic function, so anywhere inside the function body where there's a `T`, we'll replace it with some concrete type when the function is actually called. (Or actually, when it's compiled. We'll compile one version of this function for each type it is used with.) 39 | 40 | If you actually try to compile the above though, `rustc` will complain. The problem is that `T` here could be an `i32` or a `u64`... but it could also be a `struct` or an `enum`. Inside the function we do `item > largest` - how would we decide if one struct was larger than another? We need to restrict what kinds of types can be used in place of T with a _trait bound_. In this case we only want to allow T to be a type that implements the `str::cmp::PartialOrd` trait. Types that implement this trait can be compared to each other: 41 | 42 | ```rust 43 | fn largest(list: &[T]) -> &T { 44 | ``` 45 | 46 | :::info 47 | Why a single letter `T` for the generic type? It doesn't have to be; you can use `fn largest...` instead, and it will work. But in almost every language that supports something like generics, the convention is to use a single character. 48 | ::: 49 | 50 | ## In Struct Definitions 51 | 52 | Generics aren't just for functions, we can also use them in structs. Here we have a `Point` struct which has an x and a y. Both x and y are type `T`, so they must both be the same type: 53 | 54 | ```rust 55 | struct Point { 56 | x: T, 57 | y: T, 58 | } 59 | 60 | fn main() { 61 | let integer = Point { x: 5, y: 10 }; 62 | let unsigned: Point = Point { x: 9, y: 20 }; 63 | let float = Point { x: 1.0, y: 4.0 }; 64 | 65 | // This won't work, because we're trying to use two different types 66 | let wont_work = Point { x: 5, y: 4.0 }; 67 | } 68 | ``` 69 | 70 | If we want to support mixed types we can, but we'll have to redefine the struct to allow it: 71 | 72 | ```rust 73 | struct MultiPoint { 74 | x: T, 75 | y: U, 76 | } 77 | ``` 78 | 79 | ## In Method Definitions 80 | 81 | If we create a struct with generic properties, it makes sense that we'll have to define methods that are generic too: 82 | 83 | ```rust 84 | pub struct Point { 85 | x: T, 86 | y: T, 87 | } 88 | 89 | impl Point { 90 | pub fn x(&self) -> &T { 91 | &self.x 92 | } 93 | } 94 | ``` 95 | 96 | Note the `impl` - we need the `` here to let the compiler know that `T` is not a concrete type. Why? Because we can also declare methods only on specific concrete versions of a generic struct. This will add a `distance_from_origin` to `Point`, but not to any other Point, such as `Point`: 97 | 98 | ```rust 99 | impl Point { 100 | fn distance_from_origin(&self) -> f32 { 101 | (self.x.powi(2) + self.y.powi(2)).sqrt() 102 | } 103 | } 104 | ``` 105 | 106 | We can also add generics to a method that are unrelated to the generics from the struct. Here we have a `Point` with two generic parameters called `X1` and `Y1`, and a generic method with two more `X2` and `Y2`: 107 | 108 | ```rust 109 | struct Point { 110 | x: X1, 111 | y: Y1, 112 | } 113 | 114 | impl Point { 115 | // Note that mixup takes `X2` and `Y2` generic types, 116 | // in addition to `X1` and `Y1` from the struct! 117 | fn mixup(self, other: Point) -> Point { 118 | Point { 119 | x: self.x, 120 | y: other.y, 121 | } 122 | } 123 | } 124 | 125 | fn main() { 126 | let p1 = Point { x: 5, y: 10.4 }; 127 | let p2 = Point { x: "Hello", y: 'c' }; 128 | 129 | let p3 = p1.mixup(p2); 130 | 131 | // Prints "p3.x = 5, p3.y = c". 132 | println!("p3.x = {}, p3.y = {}", p3.x, p3.y); 133 | } 134 | ``` 135 | 136 | ## In Enum Definitions 137 | 138 | We've already seen a few enums that use generics such as `Option` and `Result`: 139 | 140 | ```rust 141 | enum Option { 142 | Some(T), 143 | None, 144 | } 145 | 146 | enum Result { 147 | Ok(T), 148 | Err(E), 149 | } 150 | ``` 151 | 152 | ## Performance of Code Using Generics 153 | 154 | Above we said that a you can use generics to define a function that can accept different data types, but it's perhaps more accurate to say that you can use them to easily create a whole bunch of functions, one for each data type. Much like C++ template functions, Rust generics are implemented using _monomorphization_, which is a fancy way of saying it generates a copy of your function for each generic type it was used with at compile time. 155 | 156 | In other words, if we go back to the `fn largest(list: &[T]) -> &T` we started this section with, if you were to call: 157 | 158 | ```rust 159 | let number_list = vec![34, 50, 25, 100, 65]; 160 | let result = largest(&number_list); 161 | 162 | let char_list = vec!['y', 'm', 'a', 'q']; 163 | let result = largest(&char_list); 164 | ``` 165 | 166 | then internally Rust would actually compile two different functions, a `largest` and a `largest`. This means generic have no runtime performance impact (but they do make your executable slightly larger). 167 | 168 | Continue to [10.02 - Traits](./ch10-02-traits.md). 169 | -------------------------------------------------------------------------------- /docs/ch00-intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | slug: / 4 | title: "The Rust Book (Abridged)" 5 | hide_title: true 6 | --- 7 | 8 |
9 |

The Rust Book (Abridged)

10 |

v1.0.0

11 |

By Jason Walton

12 |

Based on "The Rust Programming Language" by Steve Klabnik and Carol Nichols.

13 |
14 | 15 | PDF version of this book is available [here](https://github.com/jwalton/rust-book-abridged/releases/latest/download/rust-book-abridged.pdf). 16 | 17 | ## What is this? 18 | 19 | This is an abridged - or perhaps a better word would be condensed - version of ["The Rust Programming Language"](https://doc.rust-lang.org/stable/book/title-page.html) (AKA "the Rust Book"). This is not an original work - all the chapter names and examples in this book have been copied verbatim from the original, but all of the prose has been rewritten from scratch, leaving out anything that's not about learning Rust. This book is about 1/2 the length of the original, but I don't think it is missing anything that an experienced software developer wouldn't already know. 20 | 21 | The Rust Book is a great resource for learning Rust, especially if you're new to programming. If you fall into this category, then I strongly suggest you put this book down and go read it instead. But... the Rust Book is quite wordy. If you're already familiar with one or more other programming languages, then you are likely already familiar with a lot of the concepts the book covers, and you might benefit from this shorter version. If you are already familiar with ideas like the stack and the heap, with test driven development, with the [DRY principle](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself), then this might be a better read. 22 | 23 | This isn't meant to be a criticism of the Rust Book. It's excellent and well written, and there's a reason why it's highly recommended. The problem here is not with the original book, but more a mismatch when it comes to intended audience. 24 | 25 | ## What's different about this book? 26 | 27 | As mentioned above, the chapter names in this book are all the same as in the original, and in many cases the subsections in each chapter are the same. In most cases examples have been copied directly from the original. Keeping the original structure and examples hopefully makes it easy to jump back and forth between this book and the original, in case there are places where this book is unclear or covers concepts you are not familiar with. 28 | 29 | Where the original would build up a code example piece by piece, in most cases this version presents the finished code so you can read through it, and then points out some interesting parts. Where possible I've tried to add in material I think an advanced reader would find interesting. In some places this explains things in a different way than the original. This also adds an extra bonus chapter about async programming! 30 | 31 | I have a great deal of experience in TypeScript, Java, C/C++, Go, and a few other languages. I spent about two weeks putting this book together, reading the original, condensing it, and researching parts that weren't clear. Hopefully someone finds this useful! But I am new to Rust so if you find something that doesn't make sense, please feel free to [raise an issue](https://github.com/jwalton/rust-book-abridged). 32 | 33 | This book was written entirely by a human - none of this is generated by ChatGPT. 34 | 35 | If you enjoy this book, please [give it a star on GitHub](https://github.com/jwalton/rust-book-abridged). 36 | 37 | ## Table of Contents 38 | 39 | - [Chapter 1: Getting Started][chap1] 40 | - [Chapter 2: Guessing Game][chap2] 41 | - [Chapter 3: Common Programming Concepts][chap3] 42 | - [Chapter 4: Ownership, References, and Slices][chap4] 43 | - [Chapter 5: Using Structs to Structure Related Data][chap5] 44 | - [Chapter 6: Enums and Pattern Matching][chap6] 45 | - [Chapter 7: Managing Growing Projects with Packages, Crates, and Modules][chap7] 46 | - [Chapter 8: Common Collections][chap8] 47 | - [Chapter 9: Error Handling][chap9] 48 | - [Chapter 10: Generic Types, Traits, and Lifetimes][chap10] 49 | - [Chapter 11: Writing Automated Tests][chap11] 50 | - [Chapter 12: An I/O Project: Building a Command Line Program][chap12] 51 | - [Chapter 13: Functional Language Features: Iterators and Closures][chap13] 52 | - [Chapter 14: More About Cargo and Crates.io][chap14] 53 | - [Chapter 15: Smart Pointers][chap15] 54 | - [Chapter 16: Fearless Concurrency][chap16] 55 | - [Chapter 17: Object Oriented Features of Rust][chap17] 56 | - [Chapter 18: Patterns and Matching][chap18] 57 | - [Chapter 19: Advanced Features][chap19] 58 | - [Chapter 20: Multithreaded Web Server][chap20] 59 | - [Chapter 21: Bonus Chapter: Async Programming][chap21] 60 | 61 | [chap1]: ./ch01-getting-started.md "Chapter 1: Getting Started" 62 | [chap2]: ./ch02-guessing-game.md "Chapter 2: Guessing Game" 63 | [chap3]: ./ch03-common-programming-concepts.md "Chapter 3: Common Programming Concepts" 64 | [chap4]: ./ch04-ownership.md "Chapter 4: Ownership, References, and Slices" 65 | [chap5]: ./ch05-structs.md "Chapter 5: Using Structs to Structure Related Data" 66 | [chap6]: ./ch06-enums-and-pattern-matching.md "Chapter 6: Enums and Pattern Matching" 67 | [chap7]: ./ch07-packages-crates-modules.md "Chapter 7: Managing Growing Projects with Packages, Crates, and Modules" 68 | [chap8]: ./ch08-common-collections.md "Chapter 8: Common Collections" 69 | [chap9]: ./ch09-error-handling.md "Chapter 9: Error Handling" 70 | [chap10]: ./ch10/ch10-01-generic-data-types.md "Chapter 10: Generic Types, Traits, and Lifetimes" 71 | [chap11]: ./ch11-automated-tests.md "Chapter 11: Writing Automated Tests" 72 | [chap12]: ./ch12-io-project-cli.md "Chapter 12: An I/O Project: Building a Command Line Program" 73 | [chap13]: ./ch13-functional-language-features.md "Chapter 13: Functional Language Features: Iterators and Closures" 74 | [chap14]: ./ch14-more-about-cargo.md "Chapter 14: More About Cargo and Crates.io" 75 | [chap15]: ./ch15-smart-pointers.md "Chapter 15: Smart Pointers" 76 | [chap16]: ./ch16-fearless-concurrency.md "Chapter 16: Fearless Concurrency" 77 | [chap17]: ./ch17-object-oriented-features.md "Chapter 17: Object Oriented Features of Rust" 78 | [chap18]: ./ch18-patterns-and-matching.md "Chapter 18: Patterns and Matching" 79 | [chap19]: ./ch19/ch19-01-unsafe.md "Chapter 19: Advanced Features" 80 | [chap20]: ./ch20/ch20-01-single-threaded-web-server.md "Chapter 20: Multithreaded Web Server" 81 | [chap21]: ./ch21-async.md "Chapter 21: Bonus Chapter: Async Programming" 82 | 83 | (This version of this book is based on [commit c06006](https://github.com/rust-lang/book/commit/c06006157b14b3d47b5c716fc392b77f3b2e21ce)). 84 | -------------------------------------------------------------------------------- /static/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/ch21-async-web-server/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 10 | 11 | [[package]] 12 | name = "bitflags" 13 | version = "1.3.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 16 | 17 | [[package]] 18 | name = "bytes" 19 | version = "1.4.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 22 | 23 | [[package]] 24 | name = "cfg-if" 25 | version = "1.0.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 28 | 29 | [[package]] 30 | name = "hello-async" 31 | version = "0.1.0" 32 | dependencies = [ 33 | "tokio", 34 | ] 35 | 36 | [[package]] 37 | name = "hermit-abi" 38 | version = "0.2.6" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 41 | dependencies = [ 42 | "libc", 43 | ] 44 | 45 | [[package]] 46 | name = "libc" 47 | version = "0.2.142" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" 50 | 51 | [[package]] 52 | name = "lock_api" 53 | version = "0.4.9" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 56 | dependencies = [ 57 | "autocfg", 58 | "scopeguard", 59 | ] 60 | 61 | [[package]] 62 | name = "log" 63 | version = "0.4.17" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 66 | dependencies = [ 67 | "cfg-if", 68 | ] 69 | 70 | [[package]] 71 | name = "mio" 72 | version = "0.8.6" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" 75 | dependencies = [ 76 | "libc", 77 | "log", 78 | "wasi", 79 | "windows-sys", 80 | ] 81 | 82 | [[package]] 83 | name = "num_cpus" 84 | version = "1.15.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 87 | dependencies = [ 88 | "hermit-abi", 89 | "libc", 90 | ] 91 | 92 | [[package]] 93 | name = "parking_lot" 94 | version = "0.12.1" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 97 | dependencies = [ 98 | "lock_api", 99 | "parking_lot_core", 100 | ] 101 | 102 | [[package]] 103 | name = "parking_lot_core" 104 | version = "0.9.7" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" 107 | dependencies = [ 108 | "cfg-if", 109 | "libc", 110 | "redox_syscall", 111 | "smallvec", 112 | "windows-sys", 113 | ] 114 | 115 | [[package]] 116 | name = "pin-project-lite" 117 | version = "0.2.9" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 120 | 121 | [[package]] 122 | name = "proc-macro2" 123 | version = "1.0.56" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 126 | dependencies = [ 127 | "unicode-ident", 128 | ] 129 | 130 | [[package]] 131 | name = "quote" 132 | version = "1.0.26" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 135 | dependencies = [ 136 | "proc-macro2", 137 | ] 138 | 139 | [[package]] 140 | name = "redox_syscall" 141 | version = "0.2.16" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 144 | dependencies = [ 145 | "bitflags", 146 | ] 147 | 148 | [[package]] 149 | name = "scopeguard" 150 | version = "1.1.0" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 153 | 154 | [[package]] 155 | name = "signal-hook-registry" 156 | version = "1.4.1" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 159 | dependencies = [ 160 | "libc", 161 | ] 162 | 163 | [[package]] 164 | name = "smallvec" 165 | version = "1.10.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 168 | 169 | [[package]] 170 | name = "socket2" 171 | version = "0.4.9" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" 174 | dependencies = [ 175 | "libc", 176 | "winapi", 177 | ] 178 | 179 | [[package]] 180 | name = "syn" 181 | version = "2.0.15" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" 184 | dependencies = [ 185 | "proc-macro2", 186 | "quote", 187 | "unicode-ident", 188 | ] 189 | 190 | [[package]] 191 | name = "tokio" 192 | version = "1.27.0" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" 195 | dependencies = [ 196 | "autocfg", 197 | "bytes", 198 | "libc", 199 | "mio", 200 | "num_cpus", 201 | "parking_lot", 202 | "pin-project-lite", 203 | "signal-hook-registry", 204 | "socket2", 205 | "tokio-macros", 206 | "windows-sys", 207 | ] 208 | 209 | [[package]] 210 | name = "tokio-macros" 211 | version = "2.0.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" 214 | dependencies = [ 215 | "proc-macro2", 216 | "quote", 217 | "syn", 218 | ] 219 | 220 | [[package]] 221 | name = "unicode-ident" 222 | version = "1.0.8" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 225 | 226 | [[package]] 227 | name = "wasi" 228 | version = "0.11.0+wasi-snapshot-preview1" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 231 | 232 | [[package]] 233 | name = "winapi" 234 | version = "0.3.9" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 237 | dependencies = [ 238 | "winapi-i686-pc-windows-gnu", 239 | "winapi-x86_64-pc-windows-gnu", 240 | ] 241 | 242 | [[package]] 243 | name = "winapi-i686-pc-windows-gnu" 244 | version = "0.4.0" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 247 | 248 | [[package]] 249 | name = "winapi-x86_64-pc-windows-gnu" 250 | version = "0.4.0" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 253 | 254 | [[package]] 255 | name = "windows-sys" 256 | version = "0.45.0" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 259 | dependencies = [ 260 | "windows-targets", 261 | ] 262 | 263 | [[package]] 264 | name = "windows-targets" 265 | version = "0.42.2" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 268 | dependencies = [ 269 | "windows_aarch64_gnullvm", 270 | "windows_aarch64_msvc", 271 | "windows_i686_gnu", 272 | "windows_i686_msvc", 273 | "windows_x86_64_gnu", 274 | "windows_x86_64_gnullvm", 275 | "windows_x86_64_msvc", 276 | ] 277 | 278 | [[package]] 279 | name = "windows_aarch64_gnullvm" 280 | version = "0.42.2" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 283 | 284 | [[package]] 285 | name = "windows_aarch64_msvc" 286 | version = "0.42.2" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 289 | 290 | [[package]] 291 | name = "windows_i686_gnu" 292 | version = "0.42.2" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 295 | 296 | [[package]] 297 | name = "windows_i686_msvc" 298 | version = "0.42.2" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 301 | 302 | [[package]] 303 | name = "windows_x86_64_gnu" 304 | version = "0.42.2" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 307 | 308 | [[package]] 309 | name = "windows_x86_64_gnullvm" 310 | version = "0.42.2" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 313 | 314 | [[package]] 315 | name = "windows_x86_64_msvc" 316 | version = "0.42.2" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 319 | -------------------------------------------------------------------------------- /docs/zz-appendix/appendix-03-derivable-traits.md: -------------------------------------------------------------------------------- 1 | # Appendix C: Derivable Traits 2 | 3 | This appendix was copied directly from ["The Rust Programming Language"](https://doc.rust-lang.org/stable/book/appendix-03-derivable-traits.html). 4 | 5 | In various places in the book, we've discussed the `derive` attribute, which 6 | you can apply to a struct or enum definition. The `derive` attribute generates 7 | code that will implement a trait with its own default implementation on the 8 | type you've annotated with the `derive` syntax. 9 | 10 | In this appendix, we provide a reference of all the traits in the standard 11 | library that you can use with `derive`. Each section covers: 12 | 13 | * What operators and methods deriving this trait will enable 14 | * What the implementation of the trait provided by `derive` does 15 | * What implementing the trait signifies about the type 16 | * The conditions in which you're allowed or not allowed to implement the trait 17 | * Examples of operations that require the trait 18 | 19 | If you want different behavior from that provided by the `derive` attribute, 20 | consult the [standard library documentation](https://doc.rust-lang.org/std/index.html) 21 | for each trait for details of how to manually implement them. 22 | 23 | These traits listed here are the only ones defined by the standard library that 24 | can be implemented on your types using `derive`. Other traits defined in the 25 | standard library don't have sensible default behavior, so it's up to you to 26 | implement them in the way that makes sense for what you're trying to accomplish. 27 | 28 | An example of a trait that can't be derived is `Display`, which handles 29 | formatting for end users. You should always consider the appropriate way to 30 | display a type to an end user. What parts of the type should an end user be 31 | allowed to see? What parts would they find relevant? What format of the data 32 | would be most relevant to them? The Rust compiler doesn't have this insight, so 33 | it can't provide appropriate default behavior for you. 34 | 35 | The list of derivable traits provided in this appendix is not comprehensive: 36 | libraries can implement `derive` for their own traits, making the list of 37 | traits you can use `derive` with truly open-ended. Implementing `derive` 38 | involves using a procedural macro, which is covered in the 39 | ["Macros"][macros] section of Chapter 19. 40 | 41 | ## `Debug` for Programmer Output 42 | 43 | The `Debug` trait enables debug formatting in format strings, which you 44 | indicate by adding `:?` within `{}` placeholders. 45 | 46 | The `Debug` trait allows you to print instances of a type for debugging 47 | purposes, so you and other programmers using your type can inspect an instance 48 | at a particular point in a program's execution. 49 | 50 | The `Debug` trait is required, for example, in use of the `assert_eq!` macro. 51 | This macro prints the values of instances given as arguments if the equality 52 | assertion fails so programmers can see why the two instances weren't equal. 53 | 54 | ## `PartialEq` and `Eq` for Equality Comparisons 55 | 56 | The `PartialEq` trait allows you to compare instances of a type to check for 57 | equality and enables use of the `==` and `!=` operators. 58 | 59 | Deriving `PartialEq` implements the `eq` method. When `PartialEq` is derived on 60 | structs, two instances are equal only if *all* fields are equal, and the 61 | instances are not equal if any fields are not equal. When derived on enums, 62 | each variant is equal to itself and not equal to the other variants. 63 | 64 | The `PartialEq` trait is required, for example, with the use of the 65 | `assert_eq!` macro, which needs to be able to compare two instances of a type 66 | for equality. 67 | 68 | The `Eq` trait has no methods. Its purpose is to signal that for every value of 69 | the annotated type, the value is equal to itself. The `Eq` trait can only be 70 | applied to types that also implement `PartialEq`, although not all types that 71 | implement `PartialEq` can implement `Eq`. One example of this is floating point 72 | number types: the implementation of floating point numbers states that two 73 | instances of the not-a-number (`NaN`) value are not equal to each other. 74 | 75 | An example of when `Eq` is required is for keys in a `HashMap` so the 76 | `HashMap` can tell whether two keys are the same. 77 | 78 | ## `PartialOrd` and `Ord` for Ordering Comparisons 79 | 80 | The `PartialOrd` trait allows you to compare instances of a type for sorting 81 | purposes. A type that implements `PartialOrd` can be used with the `<`, `>`, 82 | `<=`, and `>=` operators. You can only apply the `PartialOrd` trait to types 83 | that also implement `PartialEq`. 84 | 85 | Deriving `PartialOrd` implements the `partial_cmp` method, which returns an 86 | `Option` that will be `None` when the values given don't produce an 87 | ordering. An example of a value that doesn't produce an ordering, even though 88 | most values of that type can be compared, is the not-a-number (`NaN`) floating 89 | point value. Calling `partial_cmp` with any floating point number and the `NaN` 90 | floating point value will return `None`. 91 | 92 | When derived on structs, `PartialOrd` compares two instances by comparing the 93 | value in each field in the order in which the fields appear in the struct 94 | definition. When derived on enums, variants of the enum declared earlier in the 95 | enum definition are considered less than the variants listed later. 96 | 97 | The `PartialOrd` trait is required, for example, for the `gen_range` method 98 | from the `rand` crate that generates a random value in the range specified by a 99 | range expression. 100 | 101 | The `Ord` trait allows you to know that for any two values of the annotated 102 | type, a valid ordering will exist. The `Ord` trait implements the `cmp` method, 103 | which returns an `Ordering` rather than an `Option` because a valid 104 | ordering will always be possible. You can only apply the `Ord` trait to types 105 | that also implement `PartialOrd` and `Eq` (and `Eq` requires `PartialEq`). When 106 | derived on structs and enums, `cmp` behaves the same way as the derived 107 | implementation for `partial_cmp` does with `PartialOrd`. 108 | 109 | An example of when `Ord` is required is when storing values in a `BTreeSet`, 110 | a data structure that stores data based on the sort order of the values. 111 | 112 | ## `Clone` and `Copy` for Duplicating Values 113 | 114 | The `Clone` trait allows you to explicitly create a deep copy of a value, and 115 | the duplication process might involve running arbitrary code and copying heap 116 | data. See the [“Ways Variables and Data Interact: 117 | Clone”][ways-variables-and-data-interact-clone] section in 118 | Chapter 4 for more information on `Clone`. 119 | 120 | Deriving `Clone` implements the `clone` method, which when implemented for the 121 | whole type, calls `clone` on each of the parts of the type. This means all the 122 | fields or values in the type must also implement `Clone` to derive `Clone`. 123 | 124 | An example of when `Clone` is required is when calling the `to_vec` method on a 125 | slice. The slice doesn't own the type instances it contains, but the vector 126 | returned from `to_vec` will need to own its instances, so `to_vec` calls 127 | `clone` on each item. Thus, the type stored in the slice must implement `Clone`. 128 | 129 | The `Copy` trait allows you to duplicate a value by only copying bits stored on 130 | the stack; no arbitrary code is necessary. See the ["Stack-Only Data: 131 | Copy"][stack-only-data-copy] section in Chapter 4 for more 132 | information on `Copy`. 133 | 134 | The `Copy` trait doesn't define any methods to prevent programmers from 135 | overloading those methods and violating the assumption that no arbitrary code 136 | is being run. That way, all programmers can assume that copying a value will be 137 | very fast. 138 | 139 | You can derive `Copy` on any type whose parts all implement `Copy`. A type that 140 | implements `Copy` must also implement `Clone`, because a type that implements 141 | `Copy` has a trivial implementation of `Clone` that performs the same task as 142 | `Copy`. 143 | 144 | The `Copy` trait is rarely required; types that implement `Copy` have 145 | optimizations available, meaning you don't have to call `clone`, which makes 146 | the code more concise. 147 | 148 | Everything possible with `Copy` you can also accomplish with `Clone`, but the 149 | code might be slower or have to use `clone` in places. 150 | 151 | ## `Hash` for Mapping a Value to a Value of Fixed Size 152 | 153 | The `Hash` trait allows you to take an instance of a type of arbitrary size and 154 | map that instance to a value of fixed size using a hash function. Deriving 155 | `Hash` implements the `hash` method. The derived implementation of the `hash` 156 | method combines the result of calling `hash` on each of the parts of the type, 157 | meaning all fields or values must also implement `Hash` to derive `Hash`. 158 | 159 | An example of when `Hash` is required is in storing keys in a `HashMap` 160 | to store data efficiently. 161 | 162 | ## `Default` for Default Values 163 | 164 | The `Default` trait allows you to create a default value for a type. Deriving 165 | `Default` implements the `default` function. The derived implementation of the 166 | `default` function calls the `default` function on each part of the type, 167 | meaning all fields or values in the type must also implement `Default` to 168 | derive `Default`. 169 | 170 | The `Default::default` function is commonly used in combination with the struct 171 | update syntax discussed in the [“Creating Instances From Other Instances With 172 | Struct Update 173 | Syntax”][creating-instances-from-other-instances-with-struct-update-syntax] 174 | section in Chapter 5. You can customize a few fields of a struct and then 175 | set and use a default value for the rest of the fields by using 176 | `..Default::default()`. 177 | 178 | The `Default` trait is required when you use the method `unwrap_or_default` on 179 | `Option` instances, for example. If the `Option` is `None`, the method 180 | `unwrap_or_default` will return the result of `Default::default` for the type 181 | `T` stored in the `Option`. 182 | 183 | [creating-instances-from-other-instances-with-struct-update-syntax]: ../ch05-structs.md#creating-instances-from-other-instances-with-struct-update-syntax 184 | [stack-only-data-copy]: ../ch04-ownership.md#stack-only-data-copy 185 | [ways-variables-and-data-interact-clone]: ../ch04-ownership.md#there-can-only-be-one 186 | [macros]: ../ch19/ch19-05-macros.md#195---macros 187 | -------------------------------------------------------------------------------- /docs/ch05-structs.md: -------------------------------------------------------------------------------- 1 | # 5 - Using Structs to Structure Related Data 2 | 3 | ## 5.1 - Defining and Instantiating Structs 4 | 5 | If you're coming from Go, then a struct in Rust is very similar to a struct in Go. It has public and private fields and you can define methods on a struct. If you're coming from JavaScript or Java, then a struct in Rust is similar to a class, except that a struct can't inherit from another struct. In any of these languages, a trait is very similar to an `interface`. 6 | 7 | If you're coming from C/C++, then a Rust struct is sort of like a C struct except you can add methods to it like a C++ class. If you're coming from some other language, I'm going to assume the concept of a `struct` or "object" isn't totally foreign to you. 8 | 9 | Here's what a struct looks like in Rust: 10 | 11 | ```rust 12 | struct User { 13 | active: bool, 14 | username: String, 15 | email: String, 16 | sign_in_count: u64, 17 | } 18 | 19 | fn main() { 20 | // Create an instance of User 21 | let mut myUser = User { 22 | active: true, 23 | username: String::from("jwalton"), 24 | email: String::from("jwalton@example.com"), 25 | sign_in_count: 1, 26 | }; 27 | 28 | // Access fields with `.` 29 | println!("Name: {}", myUser.username); 30 | 31 | // Variable must be declared as `mut` if we want to be 32 | // able to modify the structure. 33 | myUser.email = String::from("other_email@example,com"); 34 | } 35 | ``` 36 | 37 | Note if you want to modify the contents of a struct, is has to be marked as `mut`. You can't mark individual fields as mutable - either the whole structure is, or none of it is. 38 | 39 | :::tip 40 | If you're curious about how Rust structures are laid out in memory, check [the Rust Reference's section on Type Layout](https://doc.rust-lang.org/reference/type-layout.html). 41 | ::: 42 | 43 | ### Using the Field Init Shorthand 44 | 45 | Much like in JavaScript, we can initialize fields with a shorthand: 46 | 47 | ```rust 48 | fn build_user(email: String, username: String) -> User { 49 | User { 50 | active: true, 51 | // Instead of `username: username,` we can do: 52 | username, 53 | email, 54 | sign_in_count: 1, 55 | } 56 | } 57 | ``` 58 | 59 | ### Creating Instances from Other Instances with Struct Update Syntax 60 | 61 | Rust has something called the _struct update syntax_ which allows us to copy fields from another struct (and which is very similar to the spread operator in JavaScript). This example will set the email of `user2`, and then copy all other fields from `user1`: 62 | 63 | ```rust 64 | let user2 = User { 65 | email: String::from("yet_another_email@example.com"), 66 | ..user1 67 | } 68 | ``` 69 | 70 | When you store a field in a structure, or use the struct update syntax as in this example, from an ownership perspective you are moving that field. In this example, once we create `user2`, we can no longer use `user1` because its username field has been moved. If we had given `user2` an `email` and a `username`, then all the remaining fields we assigned from `user1` would be basic data types that implement the `Copy` trait. In this case, nothing would move, so `user1` would still be valid. 71 | 72 | ### Using Tuple Structs Without Named Fields to Create Different Types 73 | 74 | _Tuple structs_ are basically named tuples: 75 | 76 | ```rust 77 | struct Color(i32, i32, i32); 78 | struct Point(i32, i32, i32); 79 | 80 | fn main() { 81 | let black = Color(0, 0, 0); 82 | let origin = Point(0, 0, 0); 83 | } 84 | ``` 85 | 86 | Note here that `Color` and `Point` are two different types, even though they have the same structure. If you have a function that accepted a `Color`, the compiler will complain if you try to pass in a `Point`. 87 | 88 | ### Unit-Like Structs Without Any Fields 89 | 90 | You can define a struct without any fields. These are used when you want to implement some trait ([see chapter 10][chap10]) but you don't have any data you actually want to store in your struct: 91 | 92 | ```rust 93 | struct AlwaysEqual; 94 | 95 | fn main() { 96 | let subject = AlwaysEqual; 97 | } 98 | ``` 99 | 100 | You don't even need the `{}` to create an instance of `AlwaysEqual`. 101 | 102 | ### Ownership of Struct Data 103 | 104 | In the `User` struct above, we used a `String` type in the struct for the `username` and `email`. This means that the struct owns that `String`. When the struct is dropped, those two strings will be dropped too. We could instead have used an `&String` or `&str`, in which case the struct would store a reference to the string, and wouldn't own the string directly. The struct would be borrowing the string. We're not going to show an example of this though, because to do this we need something called a _lifetime annotation_, which we'll talk about in [chapter 10][chap10]. 105 | 106 | ## 5.2 - An Example Program Using Structs 107 | 108 | A quick example of a program that uses a struct: 109 | 110 | ```rust 111 | struct Rectangle { 112 | width: u32, 113 | height: u32, 114 | } 115 | 116 | fn main() { 117 | let rect1 = Rectangle { 118 | width: 30, 119 | height: 50, 120 | }; 121 | 122 | println!( 123 | "The area of the rectangle is {} square pixels.", 124 | area(&rect1) 125 | ); 126 | } 127 | 128 | fn area(rectangle: &Rectangle) -> u32 { 129 | rectangle.width * rectangle.height 130 | } 131 | ``` 132 | 133 | `area` takes an immutable reference to the `Rectangle` struct. We know when we call `area`, it won't modify our struct (even if `rect1` was declared as mutable in the caller). Passing a reference means the caller will retain ownership. Also, accessing fields on the borrowed struct doesn't move them. 134 | 135 | ### Adding Useful Functionality with Derived Traits 136 | 137 | It would be cool if we could "print" a rectangle: 138 | 139 | ```rust 140 | let rect1 = Rectangle { 141 | width: 30, 142 | height: 50, 143 | }; 144 | 145 | println!("A rectangle: {}", rect1); // This will error. 146 | ``` 147 | 148 | In Java and JavaScript we could to this with a `toString` method. In Go we could implement the `Stringer` interface. In Rust we have two different traits we can implement: `std::fmt::Display` and `Debug`. The Debug trait is one that's intended, as the name suggests, for debugging and it's the one we want here. 149 | 150 | Instead of implementing this trait ourselves, we can _derive_ this trait, which is a fancy way of saying we can let Rust generate the code for this trait for us: 151 | 152 | ```rust 153 | #[derive(Debug)] 154 | struct Rectangle { 155 | width: u32, 156 | height: u32, 157 | } 158 | 159 | fn main() { 160 | let rect1 = Rectangle { 161 | width: 30, 162 | height: 50, 163 | }; 164 | 165 | println!("A rectangle: {:?}", rect1); 166 | } 167 | ``` 168 | 169 | If you run this, it will print: 170 | 171 | ```txt 172 | A rectangle: Rectangle { width: 30, height: 50 } 173 | ``` 174 | 175 | The placeholder in `println!` has changed from `{}` to `{:?}`, which lets `println!` know we want the debug output format. We could also use `{:#?}` to "pretty print" the output. 176 | 177 | There's also a handy macro called `dbg!` which will pretty-print the value, and the file name and source line. `dbg!(&rect1);` would print something like: 178 | 179 | ```txt 180 | [src/main.rs:13] &rect1 = Rectangle { 181 | width: 30, 182 | height: 50, 183 | } 184 | ``` 185 | 186 | Note that unlike `println!`, `dbg!` takes ownership of the value passed in, so we pass a reference to `rect1` instead of passing `rect1` directly to prevent this. There are a number of other derivable traits - see [Appendix C][appc]. And, again, to learn more about traits see [chapter 10][chap10]. 187 | 188 | ## 5.3 - Method Syntax 189 | 190 | _Methods_ are functions defined on a struct. Their first parameter is always `self`, which represents the instance of the struct the method is being called on (similar to `this` in other languages). 191 | 192 | ```rust 193 | #[derive(Debug)] 194 | struct Rectangle { 195 | width: u32, 196 | height: u32, 197 | } 198 | 199 | impl Rectangle { 200 | fn area(&self) -> u32 { 201 | self.width * self.height 202 | } 203 | 204 | // Returns true if `other` Rectangle can fit inside this one. 205 | fn can_hold(&self, other: &Rectangle) -> bool { 206 | self.width > other.width && self.height > other.height 207 | } 208 | } 209 | 210 | fn main() { 211 | let rect1 = Rectangle { 212 | width: 30, 213 | height: 50, 214 | }; 215 | 216 | println!( 217 | "The area of the rectangle is {} square pixels.", 218 | rect1.area() 219 | ); 220 | } 221 | ``` 222 | 223 | The `impl` (implementation) block defines methods and associated functions on the Rectangle type. `area` takes a reference to `&self`, which is actually a short form for `self: &Self`. 224 | 225 | A method will generally have one of three different parameters for `self`: 226 | 227 | - `fn method(&self)` is a method with an immutable borrow of self. Like any other parameter, it is immutable by default. 228 | - `fn method(& mut self)` is a method with a mutable borrow of self, which means this method can change fields on `self`. 229 | - `fn method(mut self)` is a method that takes ownership of self. These methods you won't see as often, because once you call such a method the receiver is no longer valid, but these methods are often used to transform a value into some other structure. An example is the `map` method on iterator, which destroys the original iterator and returns a new one. 230 | 231 | You can have a method on a struct with the same name as one of the fields. This is most commonly used to add a _getter_ method to a struct. You can make it so a rectangle has a private `width: u32` field, and a public `width(): u32` method, which effectively makes `width` read-only. (What are public and private fields and methods? You'll have to wait for [chapter 7][chap7].) 232 | 233 | ### Automatic Referencing and Dereferencing 234 | 235 | You may have noticed that `area` takes a ref to self, but we called it as `rect1.area()` and not `(&rect1).area()`. Much like in Go, Rust has automatic referencing and dereferencing. When you call a method on a struct, Rust will automatically add in the `&`, `&mut`, or `*` so the object matches the signature of the method. 236 | 237 | Continue to [chapter 6][chap6]. 238 | 239 | [chap6]: ./ch06-enums-and-pattern-matching.md "Chapter 6: Enums and Pattern Matching" 240 | [chap7]: ./ch07-packages-crates-modules.md "Chapter 7: Managing Growing Projects with Packages, Crates, and Modules" 241 | [chap10]: ./ch10/ch10-01-generic-data-types.md "Chapter 10: Generic Types, Traits, and Lifetimes" 242 | [appc]: ./zz-appendix/appendix-03-derivable-traits.md 243 | -------------------------------------------------------------------------------- /docs/ch20/ch20-02-multi-threaded-web-server.md: -------------------------------------------------------------------------------- 1 | # 20.2 - Turning Our Single-Threaded Server into a Multithreaded Server 2 | 3 | ## Simulating a Slow Request in the Current Server Implementation 4 | 5 | Since our web server is single threaded, it will completely handle one request before moving on to the next request in the queue. If we had a request that took a long time to process, it would hold up all the subsequent requests. 6 | 7 | ```rust title="src/main.rs" 8 | use std::{ 9 | fs, 10 | io::{prelude::*, BufReader}, 11 | net::{TcpListener, TcpStream}, 12 | thread, 13 | time::Duration, 14 | }; 15 | 16 | // --snip-- 17 | 18 | fn handle_connection(mut stream: TcpStream) { 19 | // --snip-- 20 | 21 | match &request_line[..] { 22 | "GET / HTTP/1.1" => send_response(stream, 200, "OK", "hello.html"), 23 | "GET /sleep HTTP/1.1" => { 24 | thread::sleep(Duration::from_secs(5)); 25 | send_response(stream, 200, "OK", "hello.html"); 26 | } 27 | _ => send_response(stream, 404, "NOT FOUND", "404.html") 28 | } 29 | 30 | // --snip-- 31 | } 32 | ``` 33 | 34 | We've switched from an `if` to a `match`, and added a "/sleep" route. We have to pass `&request_line[..]` to the match expression to explicitly convert it to a slice here, because `match` doesn't do automatic dereferencing like the equality method does. 35 | 36 | The important thing here is, if you open up your browser and try to load [http://localhost:7878/sleep](http://localhost:7878/sleep), it'll take about five seconds for the page to load. If you tap CTRL-R to reload the page twice in quick succession, it will take about 10 seconds! Your browser sent two requests, and is waiting for the second one to finish. 37 | 38 | ## Improving Throughput with a Thread Pool 39 | 40 | We _could_ solve this problem by just creating a new thread for each incoming connection: 41 | 42 | ```rust 43 | for stream in listener.incoming() { 44 | let stream = stream.unwrap(); 45 | 46 | thread::spawn(|| { 47 | handle_connection(stream); 48 | }); 49 | } 50 | ``` 51 | 52 | Starting up an OS level thread has some costs associated with it, and if we start up too many of them we may run out of system resources, so a common pattern for a situation like this is to use a _thread pool_. We pre-allocate a number of threads that will be sitting idle, and then whenever a request comes in we hand it off to an idle worker from the pool. 53 | 54 | ```rust 55 | let pool = ThreadPool::new(4); 56 | for stream in listener.incoming() { 57 | let stream = stream.unwrap(); 58 | 59 | pool.execute(|| { 60 | handle_connection(stream); 61 | }); 62 | } 63 | ``` 64 | 65 | That's all there is too it! Except Rust can't find the `ThreadPool` symbol. We'll have to bring it into scope to use it, but before that we'll have to build a ThreadPool! 66 | 67 | ## Building a ThreadPool 68 | 69 | Before we show the code for a ThreadPool, let's take a moment to think through what it's going to look like. We want to store a collection of threads. We won't know the number of threads until runtime so a vector is a reasonable choice here, but what exactly is being stored in the vector? How do you store a thread? If we have a look at the signature for `thread::spawn`: 70 | 71 | ```rust 72 | pub fn spawn(f: F) -> JoinHandle 73 | where 74 | F: FnOnce() -> T, 75 | F: Send + 'static, 76 | T: Send + 'static, 77 | { 78 | // --snip-- 79 | } 80 | ``` 81 | 82 | We can see it returns a `JoinHandle`. The `T` here is the type the thread will "return" when it completes, but our threads are never going to complete, so we'll store a `Vec>`. Actually, in order to make our lives a little easier at debugging time, we'll give each thread a unique ID and combine ID and `JoinHandle<()>` into a `Worker` and then store a `Vec`. 83 | 84 | Here's what the `Worker` is going to look like: 85 | 86 | ```rust 87 | struct Worker { 88 | id: usize, 89 | thread: JoinHandle<()>, 90 | } 91 | 92 | impl Worker { 93 | /// Create a new Worker with the given id. 94 | pub fn new(id: usize) -> Worker { 95 | let thread = thread::spawn(|| { 96 | todo!("Zhu Li, do the thing!"); 97 | }); 98 | 99 | Worker { id, thread } 100 | } 101 | } 102 | ``` 103 | 104 | We're going to execute jobs on these threads, but what's a job? We already know they are closures. Since we want our API to be similar to `thread::spawn`, a job is going to be the same type as `F` in `thread::spawn` above. It'll be `FnOnce()` since it's a function we want to call exactly once. It will also need to be `Send` so we can transfer it to our worker thread, and `'static` because we don't know how long the thread will take to run. So we'll define `Job` as an alias for: 105 | 106 | ```rust 107 | type Job = Box; 108 | ``` 109 | 110 | Whenever we call `pool.execute` and pass in a job, we want that job to be run by a free thread from the pool. How does this happen? What happens inside the thread we spawn inside the Worker? We've conveniently left this out of our `Worker` above. There are many ways we could do this, but the approach we will use here is to send each job over a channel. 111 | 112 | Each `Worker` will hang on to the receiver side of a channel. The thread inside a `Worker` can just iterate on the channel and execute each job it receives in series. But you may recall that the channels we've been using are from the `mpsc` library, which stands for "multiple producers, single consumer". If we're creating four threads, we could create four channels and give one receiver from each to each worker. In this case, though, we'd have to decide which sender to send a new job to. How do we know which threads are free to accept new jobs? 113 | 114 | What we really want here is the other way around: "single producer, multiple consumers". We know how to share a variable between multiple threads though; instead of having multiple channels, we can have just a single channel. We can wrap the receiver in a `Mutex`, and then wrap that in an `Arc`, and multiple threads will be able to safely call into the receiver one-at-a-time to fetch jobs. 115 | 116 | Here's the code: 117 | 118 | ```rust title="src/lib.rs" 119 | use std::{ 120 | sync::{mpsc, Arc, Mutex}, 121 | thread::{self, JoinHandle}, 122 | }; 123 | 124 | type Job = Box; 125 | 126 | pub struct ThreadPool { 127 | workers: Vec, 128 | sender: mpsc::Sender, 129 | } 130 | 131 | impl ThreadPool { 132 | /// Create a new ThreadPool. 133 | /// 134 | /// The size is the number of threads in the pool. 135 | /// 136 | /// # Panics 137 | /// 138 | /// The `new` function will panic if the size is zero. 139 | pub fn new(size: usize) -> ThreadPool { 140 | // Make sure `size` is valid. 141 | assert!(size > 0); 142 | 143 | // Create our sender and receiver 144 | let (sender, receiver) = mpsc::channel(); 145 | let receiver = Arc::new(Mutex::new(receiver)); 146 | 147 | // Create a new vector. Pre-allocate the vector 148 | // to be of length `size` so we know it can store 149 | // all of our threads. 150 | let mut workers = Vec::with_capacity(size); 151 | 152 | // Create new workers and add them to the pool. 153 | for id in 0..size { 154 | workers.push(Worker::new(id, Arc::clone(&receiver))); 155 | } 156 | 157 | ThreadPool { 158 | workers, 159 | sender, 160 | } 161 | } 162 | 163 | pub fn execute(&self, f: F) 164 | where 165 | F: FnOnce() + Send + 'static, 166 | { 167 | // Send our job to a Worker. 168 | let job = Box::new(f); 169 | self.sender.send(job).unwrap(); 170 | } 171 | } 172 | 173 | struct Worker { 174 | id: usize, 175 | thread: JoinHandle<()>, 176 | } 177 | 178 | impl Worker { 179 | /// Create a new Worker with the given id. 180 | pub fn new(id: usize, receiver: Arc>>) -> Worker { 181 | let thread = thread::spawn(move || loop { 182 | let job = receiver.lock().unwrap().recv().unwrap(); 183 | println!("Worker {id} got a job; executing."); 184 | job(); 185 | }); 186 | 187 | Worker { id, thread } 188 | } 189 | } 190 | ``` 191 | 192 | If you give this a try, it should work (although you'll get some compiler warnings)! If you visit "/sleep", wait for it to load, and then double-tap "CTRL-R" to reload the page, the page should reload in about five seconds instead of ten. If you're running into problems, check out the [code in the GitHub repo](https://github.com/jwalton/rust-book-abridged/tree/master/examples/ch20-multi-threaded-web-server). 193 | 194 | One thing you might have expected us to do in the worker was: 195 | 196 | ```rust 197 | let thread = thread::spawn(move || loop { 198 | // This is not so good... 199 | for job in receiver.lock().unwrap().iter() { 200 | println!("Worker {id} got a job; executing."); 201 | job(); 202 | } 203 | }); 204 | ``` 205 | 206 | If you give this a try, it will appear to work, but our "double-reload" example will be back to ten seconds again. Why? Because this code is equivalent to: 207 | 208 | ```rust 209 | let thread = thread::spawn(move || loop { 210 | // Take the lock on the mutex... 211 | let rx = receiver.lock().unwrap(); 212 | // Then loop forever, never giving up the lock. 213 | for job in rx.iter() { 214 | println!("Worker {id} got a job; executing."); 215 | job(); 216 | } 217 | }); 218 | ``` 219 | 220 | One thread will take the mutex and then loop with it held, so one of our threads doing all the work. 221 | 222 | There are also a few things wrong with this code as it stands. First, we're obviously glossing over some error handling, which is fine for this example. Second, if you reload the "/sleep" route many times, you'll find eventually it will start taking a long time to load. What's happening here is that we're queueing up jobs in the channel. 223 | 224 | Ideally if all the workers are busy, we'd return a 503 to let the client know we are too busy to handle the request. We could do this in a few ways; we could use the `atomic` package to increment a counter when we start a job and decrement it when we finish one, so we know how many jobs are in progress. There's also a `channel::sync_channel` which allows creating a channel with a bounded size. The sender in this case has a `try_send` which will return an error if the channel is full. This is left as an exercise for the reader. 225 | 226 | Next we'll look at how to adapt our web server to [shut down gracefully](ch20-03-graceful-shutdown.md). 227 | -------------------------------------------------------------------------------- /docs/zz-appendix/appendix-02-operators.md: -------------------------------------------------------------------------------- 1 | # Appendix B: Operators and Symbols 2 | 3 | This appendix was copied directly from ["The Rust Programming Language"](https://doc.rust-lang.org/stable/book/appendix-02-operators.html). 4 | 5 | This appendix contains a glossary of Rust's syntax, including operators and 6 | other symbols that appear by themselves or in the context of paths, generics, 7 | trait bounds, macros, attributes, comments, tuples, and brackets. 8 | 9 | ## Operators 10 | 11 | Table B-1 contains the operators in Rust, an example of how the operator would 12 | appear in context, a short explanation, and whether that operator is 13 | overloadable. If an operator is overloadable, the relevant trait to use to 14 | overload that operator is listed. 15 | 16 | ### Table B-1: Operators 17 | 18 | | Operator | Example | Explanation | Overloadable? | 19 | |----------|---------|-------------|---------------| 20 | | `!` | `ident!(...)`, `ident!{...}`, `ident![...]` | Macro expansion | | 21 | | `!` | `!expr` | Bitwise or logical complement | `Not` | 22 | | `!=` | `expr != expr` | Nonequality comparison | `PartialEq` | 23 | | `%` | `expr % expr` | Arithmetic remainder | `Rem` | 24 | | `%=` | `var %= expr` | Arithmetic remainder and assignment | `RemAssign` | 25 | | `&` | `&expr`, `&mut expr` | Borrow | | 26 | | `&` | `&type`, `&mut type`, `&'a type`, `&'a mut type` | Borrowed pointer type | | 27 | | `&` | `expr & expr` | Bitwise AND | `BitAnd` | 28 | | `&=` | `var &= expr` | Bitwise AND and assignment | `BitAndAssign` | 29 | | `&&` | `expr && expr` | Short-circuiting logical AND | | 30 | | `*` | `expr * expr` | Arithmetic multiplication | `Mul` | 31 | | `*=` | `var *= expr` | Arithmetic multiplication and assignment | `MulAssign` | 32 | | `*` | `*expr` | Dereference | `Deref` | 33 | | `*` | `*const type`, `*mut type` | Raw pointer | | 34 | | `+` | `trait + trait`, `'a + trait` | Compound type constraint | | 35 | | `+` | `expr + expr` | Arithmetic addition | `Add` | 36 | | `+=` | `var += expr` | Arithmetic addition and assignment | `AddAssign` | 37 | | `,` | `expr, expr` | Argument and element separator | | 38 | | `-` | `- expr` | Arithmetic negation | `Neg` | 39 | | `-` | `expr - expr` | Arithmetic subtraction | `Sub` | 40 | | `-=` | `var -= expr` | Arithmetic subtraction and assignment | `SubAssign` | 41 | | `->` | `fn(...) -> type`, |...| -> type | Function and closure return type | | 42 | | `.` | `expr.ident` | Member access | | 43 | | `..` | `..`, `expr..`, `..expr`, `expr..expr` | Right-exclusive range literal | `PartialOrd` | 44 | | `..=` | `..=expr`, `expr..=expr` | Right-inclusive range literal | `PartialOrd` | 45 | | `..` | `..expr` | Struct literal update syntax | | 46 | | `..` | `variant(x, ..)`, `struct_type { x, .. }` | “And the rest” pattern binding | | 47 | | `...` | `expr...expr` | (Deprecated, use `..=` instead) In a pattern: inclusive range pattern | | 48 | | `/` | `expr / expr` | Arithmetic division | `Div` | 49 | | `/=` | `var /= expr` | Arithmetic division and assignment | `DivAssign` | 50 | | `:` | `pat: type`, `ident: type` | Constraints | | 51 | | `:` | `ident: expr` | Struct field initializer | | 52 | | `:` | `'a: loop {...}` | Loop label | | 53 | | `;` | `expr;` | Statement and item terminator | | 54 | | `;` | `[...; len]` | Part of fixed-size array syntax | | 55 | | `<<` | `expr << expr` | Left-shift | `Shl` | 56 | | `<<=` | `var <<= expr` | Left-shift and assignment | `ShlAssign` | 57 | | `<` | `expr < expr` | Less than comparison | `PartialOrd` | 58 | | `<=` | `expr <= expr` | Less than or equal to comparison | `PartialOrd` | 59 | | `=` | `var = expr`, `ident = type` | Assignment/equivalence | | 60 | | `==` | `expr == expr` | Equality comparison | `PartialEq` | 61 | | `=>` | `pat => expr` | Part of match arm syntax | | 62 | | `>` | `expr > expr` | Greater than comparison | `PartialOrd` | 63 | | `>=` | `expr >= expr` | Greater than or equal to comparison | `PartialOrd` | 64 | | `>>` | `expr >> expr` | Right-shift | `Shr` | 65 | | `>>=` | `var >>= expr` | Right-shift and assignment | `ShrAssign` | 66 | | `@` | `ident @ pat` | Pattern binding | | 67 | | `^` | `expr ^ expr` | Bitwise exclusive OR | `BitXor` | 68 | | `^=` | `var ^= expr` | Bitwise exclusive OR and assignment | `BitXorAssign` | 69 | | | | pat | pat | Pattern alternatives | | 70 | | | | expr | expr | Bitwise OR | `BitOr` | 71 | | |= | var |= expr | Bitwise OR and assignment | `BitOrAssign` | 72 | | || | expr || expr | Short-circuiting logical OR | | 73 | | `?` | `expr?` | Error propagation | | 74 | 75 | ## Non-operator Symbols 76 | 77 | The following list contains all symbols that don’t function as operators; that 78 | is, they don’t behave like a function or method call. 79 | 80 | Table B-2 shows symbols that appear on their own and are valid in a variety of 81 | locations. 82 | 83 | ### Table B-2: Stand-Alone Syntax 84 | 85 | | Symbol | Explanation | 86 | |--------|-------------| 87 | | `'ident` | Named lifetime or loop label | 88 | | `...u8`, `...i32`, `...f64`, `...usize`, etc. | Numeric literal of specific type | 89 | | `"..."` | String literal | 90 | | `r"..."`, `r#"..."#`, `r##"..."##`, etc. | Raw string literal, escape characters not processed | 91 | | `b"..."` | Byte string literal; constructs an array of bytes instead of a string | 92 | | `br"..."`, `br#"..."#`, `br##"..."##`, etc. | Raw byte string literal, combination of raw and byte string literal | 93 | | `'...'` | Character literal | 94 | | `b'...'` | ASCII byte literal | 95 | | |...| expr | Closure | 96 | | `!` | Always empty bottom type for diverging functions | 97 | | `_` | "Ignored" pattern binding; also used to make integer literals readable | 98 | 99 | Table B-3 shows symbols that appear in the context of a path through the module 100 | hierarchy to an item. 101 | 102 | ### Table B-3: Path-Related Syntax 103 | 104 | | Symbol | Explanation | 105 | |--------|-------------| 106 | | `ident::ident` | Namespace path | 107 | | `::path` | Path relative to the crate root (i.e., an explicitly absolute path) | 108 | | `self::path` | Path relative to the current module (i.e., an explicitly relative path). 109 | | `super::path` | Path relative to the parent of the current module | 110 | | `type::ident`, `::ident` | Associated constants, functions, and types | 111 | | `::...` | Associated item for a type that cannot be directly named (e.g., `<&T>::...`, `<[T]>::...`, etc.) | 112 | | `trait::method(...)` | Disambiguating a method call by naming the trait that defines it | 113 | | `type::method(...)` | Disambiguating a method call by naming the type for which it’s defined | 114 | | `::method(...)` | Disambiguating a method call by naming the trait and type | 115 | 116 | Table B-4 shows symbols that appear in the context of using generic type 117 | parameters. 118 | 119 | ### Table B-4: Generics 120 | 121 | | Symbol | Explanation | 122 | |--------|-------------| 123 | | `path<...>` | Specifies parameters to generic type in a type (e.g., `Vec`) | 124 | | `path::<...>`, `method::<...>` | Specifies parameters to generic type, function, or method in an expression; often referred to as turbofish (e.g., `"42".parse::()`) | 125 | | `fn ident<...> ...` | Define generic function | 126 | | `struct ident<...> ...` | Define generic structure | 127 | | `enum ident<...> ...` | Define generic enumeration | 128 | | `impl<...> ...` | Define generic implementation | 129 | | `for<...> type` | Higher-ranked lifetime bounds | 130 | | `type` | A generic type where one or more associated types have specific assignments (e.g., `Iterator`) | 131 | 132 | Table B-5 shows symbols that appear in the context of constraining generic type 133 | parameters with trait bounds. 134 | 135 | ### Table B-5: Trait Bound Constraints 136 | 137 | | Symbol | Explanation | 138 | |--------|-------------| 139 | | `T: U` | Generic parameter `T` constrained to types that implement `U` | 140 | | `T: 'a` | Generic type `T` must outlive lifetime `'a` (meaning the type cannot transitively contain any references with lifetimes shorter than `'a`) | 141 | | `T: 'static` | Generic type `T` contains no borrowed references other than `'static` ones | 142 | | `'b: 'a` | Generic lifetime `'b` must outlive lifetime `'a` | 143 | | `T: ?Sized` | Allow generic type parameter to be a dynamically sized type | 144 | | `'a + trait`, `trait + trait` | Compound type constraint | 145 | 146 | Table B-6 shows symbols that appear in the context of calling or defining 147 | macros and specifying attributes on an item. 148 | 149 | ### Table B-6: Macros and Attributes 150 | 151 | | Symbol | Explanation | 152 | |--------|-------------| 153 | | `#[meta]` | Outer attribute | 154 | | `#![meta]` | Inner attribute | 155 | | `$ident` | Macro substitution | 156 | | `$ident:kind` | Macro capture | 157 | | `$(…)…` | Macro repetition | 158 | | `ident!(...)`, `ident!{...}`, `ident![...]` | Macro invocation | 159 | 160 | Table B-7 shows symbols that create comments. 161 | 162 | ### Table B-7: Comments 163 | 164 | | Symbol | Explanation | 165 | |--------|-------------| 166 | | `//` | Line comment | 167 | | `//!` | Inner line doc comment | 168 | | `///` | Outer line doc comment | 169 | | `/*...*/` | Block comment | 170 | | `/*!...*/` | Inner block doc comment | 171 | | `/**...*/` | Outer block doc comment | 172 | 173 | Table B-8 shows symbols that appear in the context of using tuples. 174 | 175 | ### Table B-8: Tuples 176 | 177 | | Symbol | Explanation | 178 | |--------|-------------| 179 | | `()` | Empty tuple (aka unit), both literal and type | 180 | | `(expr)` | Parenthesized expression | 181 | | `(expr,)` | Single-element tuple expression | 182 | | `(type,)` | Single-element tuple type | 183 | | `(expr, ...)` | Tuple expression | 184 | | `(type, ...)` | Tuple type | 185 | | `expr(expr, ...)` | Function call expression; also used to initialize tuple `struct`s and tuple `enum` variants | 186 | | `expr.0`, `expr.1`, etc. | Tuple indexing | 187 | 188 | Table B-9 shows the contexts in which curly braces are used. 189 | 190 | ### Table B-9: Curly Brackets 191 | 192 | | Context | Explanation | 193 | |---------|-------------| 194 | | `{...}` | Block expression | 195 | | `Type {...}` | `struct` literal | 196 | 197 | Table B-10 shows the contexts in which square brackets are used. 198 | 199 | ### Table B-10: Square Brackets 200 | 201 | | Context | Explanation | 202 | |---------|-------------| 203 | | `[...]` | Array literal | 204 | | `[expr; len]` | Array literal containing `len` copies of `expr` | 205 | | `[type; len]` | Array type containing `len` instances of `type` | 206 | | `expr[expr]` | Collection indexing. Overloadable (`Index`, `IndexMut`) | 207 | | `expr[..]`, `expr[a..]`, `expr[..b]`, `expr[a..b]` | Collection indexing pretending to be collection slicing, using `Range`, `RangeFrom`, `RangeTo`, or `RangeFull` as the "index" | 208 | -------------------------------------------------------------------------------- /docs/ch19/ch19-05-macros.md: -------------------------------------------------------------------------------- 1 | # 19.5 - Macros 2 | 3 | If you're coming to Rust from C or C++, then you're no doubt already familiar with macros. We're going to give a quick introduction to macros here, but if you want to read more you should check out [The Little Book of Rust Macros](https://veykril.github.io/tlborm/introduction.html). 4 | 5 | Macros are a kind of "metaprogramming". When we write a Macro, we're actually writing Rust code that generates more Rust code. 6 | 7 | - Macros run at compile time, so they have no runtime performance impact (although they can generate code that runs at runtime, which might). 8 | - Macros can take a variable number of parameters (such as the `println!` marco does) which normal Rust functions cannot. 9 | - Macros must be brought into scope or defined locally before they are called. 10 | 11 | ## Declarative Macros with `macro_rules!` for General Metaprogramming 12 | 13 | _Declarative macros_ are sometimes called "macros by example" or just "macros" (because these are the most common kind of macro you're going to encounter). Here is a very simple macro: 14 | 15 | ```rust 16 | macro_rules! four { 17 | () => { 18 | 1 + 3 19 | }; 20 | } 21 | 22 | fn main() { 23 | let x = four!(); 24 | println!("{x}"); 25 | } 26 | 27 | ``` 28 | 29 | The `macro_rules! four` says we're going to declare a macro named `four!`. Inside the `{}`, the rest of this macro is similar to a `match` expression (in this example we only have one arm). Each rule in a `macro_rules!` is of the format `(MATCHER) => {EXPANSION};`. When we call a macro, we don't actually pass in parameters like `i32`s or `&str`s, instead we're passing in a snippet of Rust code. When the macro runs, it will try to match the passed in token tree to each matcher in turn. Once it finds a match, we'll replace the whole macro with whatever is in the expansion part. 30 | 31 | In the case of our macro above, we just have a single "empty matcher". If you were to try calling `let x = four!("hello");`, you'd get an error telling you `` no rules expected the token `"hello"` ``. 32 | 33 | A matcher can contain _captures_ which let us capture some tokens to a _metavariable_. Metavariables start with `$`: 34 | 35 | ```rust 36 | macro_rules! add_one { 37 | ($e:expr) => { $e + 1 }; 38 | } 39 | ``` 40 | 41 | Here if you called `add_one!(2)` would be replaced with `2 + 1`. Let's have a look at the `vec!` macro, which is a bit more exciting: 42 | 43 | ```rust 44 | #[macro_export] 45 | macro_rules! vec { 46 | ( $( $x:expr ),* ) => { 47 | { 48 | let mut temp_vec = Vec::new(); 49 | $( 50 | temp_vec.push($x); 51 | )* 52 | temp_vec 53 | } 54 | }; 55 | } 56 | ``` 57 | 58 | :::info 59 | This is actually a slightly simplified version of `vec!`. The original tries to preallocate the correct amount of data in the new vector. 60 | ::: 61 | 62 | First, notice we've added the `#[macro_export]` annotation. Without this annotation, this macro can't be used outside of the crate it is defined in. 63 | 64 | The `$(),*` part of the matcher here is called a _repetition_. These have the form `$ (...) sep rep`, where `( ... )` is the part that's being repeated, `sep` is an optional separator token, and `rep` defines how many times the pattern can repeat - `?` for zero or one, `*` for zero or more, and `+` for one or more (like in a regular expression). So `( $( $x:expr ),* )` matches zero or more expressions, separated by commas, and each time through the repetition we assign the matched part to the `$x` metavariable. 65 | 66 | On the right hand side of the `=>` we have the code we're going to expand this to. Inside the `$()` is the repetition part - this code will be inserted once for each time the repetition matches on the matcher side. 67 | 68 | So if we were to write `vec![1, 2, 3]`, at compile time this would get replaced with: 69 | 70 | ```rust 71 | { 72 | let mut temp_vec = Vec::new(); 73 | temp_vec.push(1); 74 | temp_vec.push(2); 75 | temp_vec.push(3); 76 | temp_vec 77 | } 78 | ``` 79 | 80 | ## Procedural Macros for Generating Code from Attributes 81 | 82 | A _procedural macro_ is a Rust function that takes in a `TokenStream` of some input source code and produces a `TokenStream` of some generated code. There are three kinds of procedural macros: custom derive, attribute-like, and function-like. When we `#[derive()]` a trait, it's going through a custom-derive macro. Procedural macros need to be defined in their own special crate for technical reasons we're going to hand wave away for this book, although this will likely change in the future. 83 | 84 | ### How to Write a Custom `derive` Macro 85 | 86 | Let's create some new projects: 87 | 88 | ```sh 89 | mkdir projects 90 | cd projects 91 | cargo new hello_macro --lib 92 | cd hello_macro 93 | cargo new hello_macro_derive --lib 94 | ``` 95 | 96 | We created two projects, one inside the other. The outer project will contain our trait, and the inner wil going to contain our custom derive macro. We create these two projects one-inside-the-other because they are tightly related; if the code in the outer project changes, odds are the code in the inner project will too. Unfortunately we'll need to publish the two crates to [crates.io](https://crates.io) separately. 97 | 98 | In the outer project, we're going to create a trait: 99 | 100 | ```rust title="hello_macro/src/lib.rs" 101 | pub trait HelloMacro { 102 | fn hello_macro(); 103 | } 104 | ``` 105 | 106 | The idea here is that a when a consumer of our library implements this trait, we want to give them a derive macro that will implement the `hello_macro` method for them. Let's create one more project in the "projects" folder: 107 | 108 | ```sh 109 | cd .. 110 | cargo new pancakes 111 | ``` 112 | 113 | And then write a file that uses our derive macro: 114 | 115 | ```rust title="pancakes/src/main.rs" 116 | use hello_macro::HelloMacro; 117 | use hello_macro_derive::HelloMacro; 118 | 119 | // This derive attribute will run our derive macro. 120 | #[derive(HelloMacro)] 121 | struct Pancakes; 122 | 123 | fn main() { 124 | // This will print "Hello, Macro! My name is Pancakes!" 125 | Pancakes::hello_macro(); 126 | } 127 | ``` 128 | 129 | In our inner project, we're going to add some dependencies to _Cargo.toml_: 130 | 131 | ```toml title="hello_macro/hello_macro_derive/Cargo.toml" 132 | [lib] 133 | proc-macro = true 134 | 135 | [dependencies] 136 | syn = "1.0" 137 | quote = "1.0" 138 | ``` 139 | 140 | The `proc-macro = true` line [tells Cargo](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#configuring-a-target) that this is a special library that contains procedural macros. This also gives us access to the `proc_macro` crate, which is where `TokenStream` comes from. `syn` is a crate for parsing Rust code into an [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or _AST_, and `quote` is a crate for turning a syntax tree back into Rust code. `syn` is going to take our `Pancakes` data structure above and turn it into something like: 141 | 142 | ```rust 143 | DeriveInput { 144 | // --snip-- 145 | ident: Ident { 146 | ident: "Pancakes", 147 | span: #0 bytes(95..103) 148 | }, 149 | data: Struct( 150 | DataStruct { 151 | struct_token: Struct, 152 | fields: Unit, 153 | semi_token: Some( 154 | Semi 155 | ) 156 | } 157 | ) 158 | } 159 | ``` 160 | 161 | The field we care about for our implementation is the `ident` or "identifier" for our struct. You can see what else will be passed to our macro in the [`syn::DeriveInput` documentation](https://docs.rs/syn/1.0.109/syn/struct.DeriveInput.html). 162 | 163 | Here's the code for our macro: 164 | 165 | ```rust title="hello_macro/hello_macro_derive/src/lib.rs" 166 | use proc_macro::TokenStream; 167 | use quote::quote; 168 | use syn; 169 | 170 | // This line tells Rust that this is the macro 171 | // to call when someone does `#[derive(HelloMacro)]`. 172 | #[proc_macro_derive(HelloMacro)] 173 | pub fn hello_macro_derive(input: TokenStream) -> TokenStream { 174 | // Construct a representation of Rust code as a syntax tree 175 | // that we can manipulate 176 | let ast = syn::parse(input).unwrap(); 177 | 178 | // Build the trait implementation 179 | impl_hello_macro(&ast) 180 | } 181 | 182 | // It's very common to split the derive macro into one function 183 | // that parses the input (`hello_macro_derive`) and one that 184 | // generates the code (`impl_hello_macro`). 185 | fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream { 186 | let name = &ast.ident; 187 | 188 | // `#name` will be replaced with `Pancakes` here. 189 | let gen = quote! { 190 | impl HelloMacro for #name { 191 | fn hello_macro() { 192 | println!("Hello, Macro! My name is {}!", stringify!(#name)); 193 | } 194 | } 195 | }; 196 | 197 | // Convert `gen` into a `TokenStream`. 198 | gen.into() 199 | } 200 | ``` 201 | 202 | The `quote!` macro here helps us define the code we want to generate. Note the `#name` template inside of `quote!`. `quote!` has other cool template tricks, so be sure to [check out its documentation](https://docs.rs/quote/latest/quote/). The `stringify!` macro is built into rust and turns an expression like `1 + 2` into a string like `"1 + 2"`, or here `Pancakes` into `"Pancakes"`. 203 | 204 | If you want to run this, there's just one thing left to do. In our _pancakes_ project, we need to add dependencies to _Cargo.toml_ so it can find our trait and macro: 205 | 206 | ```toml title="pancakes/Cargo.toml" 207 | [dependencies] 208 | hello_macro = { path = "../hello_macro" } 209 | hello_macro_derive = { path = "../hello_macro/hello_macro_derive" } 210 | ``` 211 | 212 | Now you should be able to `cargo run` from the _pancakes_ folder. If you run into trouble, the full source is [available on GitHub](https://github.com/jwalton/rust-book-abridged/tree/master/examples/ch19-hello-macro). 213 | 214 | ### Attribute-like macros 215 | 216 | _Attribute-like_ macros are another kind of procedural macros. They let you define custom attributes, for example: 217 | 218 | ```rust 219 | #[route(GET, "/")] 220 | fn index() { 221 | ``` 222 | 223 | Unlike a custom derive macro (which can only be applied to structs and enums), these can be applied to any Rust code. To define this macro, you'd create a `macro` function like this: 224 | 225 | ```rust 226 | #[proc_macro_attribute] 227 | pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream { 228 | // --snip-- 229 | } 230 | ``` 231 | 232 | The implementation would be just like the derive macro, except that there are two `TokenStream`s - one for the item we are adding this attribute to, and one for the parameters passed to the macro. Like the derive macro, this needs to be in a special crate by itself (or with other procedural macros). 233 | 234 | ### Function-like macros 235 | 236 | The last kind of procedural macro is the _function-like_ macro. The name comes from the fact that we can call them like a function, similar to `macro_rules!` macros: 237 | 238 | ```rust 239 | let sql = sql!(SELECT * FROM posts WHERE id=1); 240 | ``` 241 | 242 | This macro would be defined as: 243 | 244 | ```rust 245 | #[proc_macro] 246 | pub fn sql(input: TokenStream) -> TokenStream { 247 | // --snip-- 248 | } 249 | ``` 250 | -------------------------------------------------------------------------------- /docs/ch06-enums-and-pattern-matching.md: -------------------------------------------------------------------------------- 1 | # 6 - Enums and Pattern Matching 2 | 3 | ## 6.1 - Defining an Enum 4 | 5 | An _enum_ allows you to define a type by enumerating its possible _variants_. If you're coming from some other language, you probably think of an enum as basically a fancy way to assign names to a set of numbers. A Rust enum goes beyond this, as each variant of an enum can have some data associated with it, much like a struct. 6 | 7 | ```rust 8 | // Define an enum. 9 | enum IpAddrKind { 10 | V4, 11 | V6, 12 | } 13 | 14 | // Use one of the variants from the enum. 15 | let ip_address_kind = IpAddrKind::V4; 16 | ``` 17 | 18 | `IpAddrKind::V4` and `IpAddrKind::V6` are both of type IpAddrKind. 19 | 20 | We can also attach data to an enum: 21 | 22 | ```rust 23 | enum Message { 24 | Quit, 25 | Move { x: i32, y: i32 }, 26 | Write(String), 27 | ChangeColor(u8, u8, u8), 28 | } 29 | 30 | fn main() { 31 | let m1 = Message::Quit; 32 | let m2 = Message::Move { x: 7, y: 9 }; 33 | let m3 = Message::Write(String::from("Hello")); 34 | let m4 = Message::ChangeColor(0, 255, 255); 35 | } 36 | ``` 37 | 38 | Note that the different variants can take different types of associated data, and the data can either have named values like a struct or a set of values like a name tuple. Importantly though, all are of type `Message`. 39 | 40 | We can also defined methods on an enum: 41 | 42 | ```rust 43 | impl Message { 44 | fn call(&self) { 45 | // method body would be defined here 46 | } 47 | } 48 | 49 | let m = Message::Write(String::from("hello")); 50 | m.call(); 51 | ``` 52 | 53 | The `call` function here might use a match statement to perform different actions based on the variant of the Message enum. 54 | 55 | ### The `Option` Enum and Its Advantages Over Null Values 56 | 57 | As mentioned earlier in this book, Rust has no `null` value. In most languages, you can dereference a null pointer, and it will cause your program to crash at runtime. Rust has a strong focus on safety and makes the conscious decision to not allow null pointers to exist in the first place. 58 | 59 | However, sometimes we have a value that might not be defined. For example we might have a "middle_name" field on a User, but some users might not have a middle name. Rust handles this with the `Option` enum, defined by the standard library. `Option` is used frequently in Rust - so frequently that both `Option` and it's two variants are in the prelude, so you don't have to `use` it to bring it into scope. 60 | 61 | `Option` is defined as: 62 | 63 | ```rust 64 | enum Option { 65 | None, 66 | Some(T), 67 | } 68 | ``` 69 | 70 | The `` means this is a generic enum - it can hold a value of any type. We'll talk more about generics in [chapter 10][chap10], but this is very similar to generics and template classes in other languages. When we use an `Option`, we have to specify a concrete type for `T` which defines a new type. For example, an `Option` can be `None`, or it can be `Some(i32)`. 71 | 72 | ```rust 73 | // No need for `std::Options::Some`, or even `Option::Some`, 74 | // because these are in the prelude. 75 | let some_number = Some(5); 76 | let some_str = Some("Hello"); 77 | 78 | // Need to explicitly annotate type here, since Rust 79 | // can't automatically infer what type to use for 80 | // `T` from `None`. 81 | let absent_number: Option = None; 82 | ``` 83 | 84 | In this example `some_number` will be of type `Option`, and `some_str` will similarly be `Option<&str>`. The `None` case here is a bit like null in a traditional language. `None` serves as a marker that a value isn't present. We're not going to be able to use use an `Option` in place of a regular `i32` though: 85 | 86 | ```rust 87 | let x = 7; 88 | let y = Some(8); 89 | 90 | // This will fail, since x and y are mismatched types 91 | let z = x + y; 92 | ``` 93 | 94 | The difference between `Option` in Rust and null in other languages is that we can't just use an `Option`, we have to explicitly handle the case where the value might not be there. What we need is a way to convert an `Option` into a `T`. If you have a look at [Option in the Rust Reference](https://doc.rust-lang.org/std/option/) you'll see that it has many methods defined on it that provide different ways to extract the underlying value from an Option, each with different ways of handling the case where the value is None. 95 | 96 | ```rust 97 | let x = Some(8); 98 | 99 | // If x is Some then use the value, otherwise panic. 100 | let must_exist = x.expect("x should never be undefined here"); 101 | 102 | // Same as expect, but uses a generic message. 103 | let must_exist_2 = x.unwrap(); 104 | 105 | // If x is Some use the value, otherwise 106 | // if x is None use 9. 107 | let with_default = x.unwrap_or(9); 108 | 109 | // If x is Some use the value + 1, otherwise use 0. 110 | // We'll talk about match more in the next section! 111 | let from_match = match x { 112 | Some(v) => v + 1, 113 | None => 0 114 | }; 115 | ``` 116 | 117 | ## 6.2 The `match` Control Flow Construct 118 | 119 | `match` is a bit like a switch/case statement on steroids. Formally, a match consists of an expression, and then one or more "arms" with patterns that try to match the expression. The pattern for each arm is evaluated in order, and the code associated with the first arm that matches will be executed. The pattern side is quite a bit more flexible than a switch/case statement (see [chapter 18][chap18]). One thing to note about a match expression is that the patterns must cover every possible value - they must be _exhaustive_. If there are any possibly-unhandled-values, the compiler will error. 120 | 121 | Here's an example of a match expression, taken directly from the original Rust Book: 122 | 123 | ```rust 124 | enum Coin { 125 | Penny, 126 | Nickel, 127 | Dime, 128 | Quarter, 129 | } 130 | 131 | fn value_in_cents(coin: Coin) -> u8 { 132 | match coin { 133 | Coin::Penny => 1, 134 | Coin::Nickel => 5, 135 | Coin::Dime => 10, 136 | Coin::Quarter => 25, 137 | } 138 | } 139 | ``` 140 | 141 | ### Patterns That Bind to Values 142 | 143 | A match expression can bind to parts of values that match the pattern. This can be used to extract one or more values out of an enum. We saw a quick example of this when handling `Option`s in the previous section: 144 | 145 | ```rust 146 | let x = Some(7); 147 | 148 | let from_match = match x { 149 | Some(v) => v + 1, 150 | None => 0 151 | }; 152 | ``` 153 | 154 | Here `v` gets bound to the contents of `Some`. Let's see this in action with our coin example from before. Let's change the Quarter variant of the Coin enum so it tells us which Canadian province this quarter is from: 155 | 156 | ```rust 157 | #[derive(Debug)] // so we can inspect the state in a minute 158 | enum Province { 159 | Alberta, 160 | BritishColumbia, 161 | // --snip-- 162 | } 163 | 164 | enum Coin { 165 | Penny, 166 | Nickel, 167 | Dime, 168 | Quarter(Province), 169 | Loonie, 170 | Toonie, 171 | } 172 | 173 | fn value_in_cents(coin: Coin) -> u8 { 174 | match coin { 175 | Coin::Penny => 1, 176 | Coin::Nickel => 5, 177 | Coin::Dime => 10, 178 | Coin::Quarter(province) => { 179 | println!("Quarter from {:?}!", province); 180 | 25 181 | } 182 | Loonie => 100, 183 | Toonie => 200, 184 | } 185 | } 186 | ``` 187 | 188 | Each arm of the match can have a simple expression, or a block of code. As with functions, if the block ends with an expression this will be used as the value for that arm. If we were to call this with a `Coin::Quarter(Province::Ontario)`, then in the Quarter arm of the match, `province` would be bound to `Province::Ontario`. 189 | 190 | Let's take a moment here to reflect on ownership implications. In this case, the Province enum implements the Copy trait, so we don't have to worry about ownership as the Province enum is going to be allocated on the stack. If we change the Coin enum so it is `Quarter(String)` however, then binding `province` inside the match would move ownership of the String out of the coin and we wouldn't be able to use it again outside the match! We could fix this by borrowing the value instead, either by changing the match expression to `match &coin` or to make `value_in_cents` take a reference to the Coin as a parameter instead of the Coin itself. 191 | 192 | ### Catch-all Patterns and the \_ Placeholder 193 | 194 | A match expression must be exhaustive - it has to cover all possible cases. Sometimes we have a number of cases we want to treat the same way, or enumerating all cases would be impractical. If we wrote a match and passed in an `i32` as the expression, we certainly wouldn't want to write out all 4 billion possible values the i32 could be! The catch-all pattern lets us create a default arm (similar to `default` in a switch/case statement in many languages). 195 | 196 | The example used by the original Rust Book is that you're building a board game where a player rolls a dice. On a roll of a 3, the place gets a hat. On a 7, the player loses their hat. On any other dice roll, they move that many spaces: 197 | 198 | ```rust 199 | let dice_roll = 9; 200 | match dice_roll { 201 | 3 => add_fancy_hat(), 202 | 7 => remove_fancy_hat(), 203 | other => move_player(other), 204 | } 205 | ``` 206 | 207 | Here we have explicit arms for the 3 and 7 case, and then we have a catch-all pattern that binds the value of `dice_roll` to `other`. If we didn't actually want to _use_ the value in the catch-all case, we wouldn't want to bind the value to a variable, since we'd get a warning from the compiler about an unused variable. In this case, we can replace `other` with `_`: 208 | 209 | ```rust 210 | match dice_roll { 211 | 3 => add_fancy_hat(), 212 | 7 => remove_fancy_hat(), 213 | _ => (), 214 | } 215 | ``` 216 | 217 | Here we're making this arm evaluate to the empty unit tuple, explicitly telling Rust that we don't want to do anything in this case. (Note that unlike `other`, `_` also doesn't bind the variable, which has ownership implications! See [chapter 18](./ch18-patterns-and-matching.md#ignoring-an-unused-variable-by-starting-its-name-with-_). 218 | 219 | ## 6.3 - Concise Control Flow with `if let` 220 | 221 | On other languages you can convert a switch/case statement into a series of if/else statements. You can do the same in Rust. You could write: 222 | 223 | ```rust 224 | let config_max = Some(3u8); 225 | match config_max { 226 | Some(max) => println!("The maximum is configured to be {}", max), 227 | _ => (), 228 | } 229 | ``` 230 | 231 | But this is a bit verbose, considering the default arm does nothing. We can rewrite this as an if statement with `if let`: 232 | 233 | ```rust 234 | let config_max = Some(3u8); 235 | if let Some(max) = config_max { 236 | println!("The maximum is configured to be {}", max); 237 | } 238 | ``` 239 | 240 | `if let` takes a pattern and an expression, separated by an equals sign, and works exactly like the arm of a switch. The downside to `if let` over a `match` statement is that the compiler does not force you to exhaustively handle every possible scenario. 241 | 242 | Continue to [chapter 7][chap7]. 243 | 244 | [chap7]: ./ch07-packages-crates-modules.md "Chapter 7: Managing Growing Projects with Packages, Crates, and Modules" 245 | [chap10]: ./ch10/ch10-01-generic-data-types.md "Chapter 10: Generic Types, Traits, and Lifetimes" 246 | [chap18]: ./ch18-patterns-and-matching.md "Chapter 18: Patterns and Matching" 247 | -------------------------------------------------------------------------------- /static/img/undraw_docusaurus_tree.svg: -------------------------------------------------------------------------------- 1 | 2 | Focus on What Matters 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /docs/ch21-async.md: -------------------------------------------------------------------------------- 1 | # 21 - Async Programming 2 | 3 | In this section we're going to re-implement our web server from [chapter 20][chap20] using async functions. We're just going to give you enough here to get your feet wet. For further reading, check out [Asynchronous Programming in Rust](https://rust-lang.github.io/async-book/), and the [Tokio Tutorial](https://tokio.rs/tokio/tutorial). As usual, if you're looking for the full source for this project, it's [in the GitHub repo](https://github.com/jwalton/rust-book-abridged/tree/master/examples/ch21-async-web-server). 4 | 5 | ## JavaScript 6 | 7 | Wait... Isn't this supposed to be a book about Rust? It is, but we're going to start this chapter off talking about JavaScript. Love it or hate it, JavaScript is the most popular language in the world, and it is probably where most people were first exposed to the idea of async programming. 8 | 9 | ```js title="user.js" 10 | // JavaScript Code 11 | import * as fs from "fs/promises"; 12 | 13 | async function getUserName() { 14 | const username = await fs.readFile("./username.txt", { encoding: "utf-8" }); 15 | console.log(`Hello ${username}`); 16 | } 17 | ``` 18 | 19 | Even if you don't know JavaScript, hopefully this example is simple enough that you can follow along. We're calling `fs.readFile` to read in a file. In JavaScript this is going to return a `Promise`. A `Promise` in JavaScript is the result of some calculation we don't know yet (similar to a `Future` in Java, or as we'll see in a moment a `Future` in Rust). The magic in this function happens at the `await` keyword. When we `await` a promise, the current function stops executing, allowing other functions to run. At some future point in time when the promise resolves, this function will continue from where it left off. 20 | 21 | In JavaScript, the above is actually more or less syntactic sugar for: 22 | 23 | ```js title="user.js" 24 | // JavaScript Code 25 | import * as fs from 'fs/promises'; 26 | 27 | function getUserName() { 28 | return fs.readFile("./username.txt", { encoding: 'utf-8' }) 29 | .then(username => console.log(`Hello ${username}`)); 30 | ``` 31 | 32 | Here it's a little easier to understand how the execution of the function can be suspended. `getUserName` calls into `readFile` which creates a promise, and then `getUserName` returns. At some future point in time, when the promise resolves, someone will call into the closure we're passing to `then`. Running this closure is how we "continue" this function in JavaScript. 33 | 34 | In Rust, we could rewrite the above example as something like: 35 | 36 | ```rust 37 | use std::{error::Error}; 38 | use tokio::fs; 39 | 40 | async fn get_user_name() -> Result<(), Box> { 41 | let username = fs::read_to_string("./username.txt").await?; 42 | println!("Hello {username}"); 43 | 44 | Ok(()) 45 | } 46 | 47 | ``` 48 | 49 | This is very similar to the JavaScript example in many ways. Here `fs::read_to_string` returns a type that implements the `Future` trait (specifically `Future>`). When we call `await` on the future, execution of this function is suspended, and at some future point someone will resume execution and the result of the `await` will be a `Result`. The `?` operator turns the `Result` into a `String`. 50 | 51 | The important things to know here are that - in JavaScript or in Rust - you can only use `await` inside a function that's declared `async`, and `await` will temporarily suspend execution of this function. 52 | 53 | ## The Runtime 54 | 55 | In our JavaScript example, we glossed over one important detail. Someone calls calls into the closure we're passing to `then`, but who is this mysterious someone? In JavaScript, everything runs in an event loop which is part of the JavaScript runtime. When the promise eventually resolves, it will queue a task and the event loop will pick it up and call into the closure. In our Rust example, we have the same problem; who takes care of restarting `get_user_name` when the `Future` from `fs::read_to_string` completes? Here again, it's the runtime. 56 | 57 | Except of course that Rust doesn't have a runtime. In Rust, the only code that runs in your application is code you write or code you bring in from a crate, so you need to either write your own runtime or pull one in from a crate! The most popular at the moment is [Tokio](https://tokio.rs/), but there are other options. Also, unlike in JavaScript where everything is single threaded on the event loop, in Rust our async runtime could be implemented on a single thread or could be multithreaded (Tokio supports both). 58 | 59 | Tokio provides us with a lot more than just a runtime. If you look at our Rust example above, you'll notice we're calling `tokio::fs::read_to_string` instead of `std::io::read_to_string`. The standard library version does the same thing, but it doesn't return a `Future`, it blocks until the file is read. If we were to use `std::io::read_to_string` here, it would block this thread for a while, potentially stopping other async code from running. Tokio provides async versions of many standard library functions in this way, and because of this, refactoring non-async code to async is usually not trivial. 60 | 61 | ## An `async` Web Server 62 | 63 | Let's write an async web server: 64 | 65 | ```sh 66 | $ cargo new hello-async 67 | $ cd hello-async 68 | ``` 69 | 70 | Update our _Cargo.toml_ to include Tokio: 71 | 72 | ```toml title="Cargo.toml" 73 | [package] 74 | name = "hello-async" 75 | version = "0.1.0" 76 | edition = "2021" 77 | 78 | [dependencies] 79 | tokio = { version = "1", features = ["full"] } 80 | ``` 81 | 82 | Notice the `features = ["full"]`. Features allow us to conditionally compile only the parts of Tokio we need. Tokio provides duplicates of most of the standard library, and if you don't need parts of it you can remove them here to make your binary smaller. Here's the code: 83 | 84 | ```rust title="src/main.rs" 85 | use std::{error::Error, time::Duration}; 86 | use tokio::{ 87 | fs, 88 | io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, 89 | net::{TcpListener, TcpStream}, 90 | }; 91 | 92 | #[tokio::main] 93 | async fn main() -> Result<(), Box> { 94 | let port = 7878u16; 95 | let listen_address = format!("127.0.0.1:{port}"); 96 | let listener = TcpListener::bind(listen_address).await.unwrap(); 97 | println!("Listening on port {}", port); 98 | 99 | loop { 100 | let (stream, _) = listener.accept().await.unwrap(); 101 | handle_connection(stream).await; 102 | } 103 | } 104 | 105 | async fn handle_connection(mut stream: TcpStream) { 106 | let buf_reader = BufReader::new(&mut stream); 107 | 108 | let mut lines = buf_reader.lines(); 109 | let request_line = lines.next_line().await.unwrap().unwrap(); 110 | 111 | println!("Incoming request for {}", request_line); 112 | 113 | match &request_line[..] { 114 | "GET / HTTP/1.1" => send_response(stream, 200, "OK", "hello.html").await, 115 | "GET /sleep HTTP/1.1" => { 116 | tokio::time::sleep(Duration::from_secs(5)).await; 117 | send_response(stream, 200, "OK", "hello.html").await; 118 | } 119 | _ => send_response(stream, 404, "NOT FOUND", "404.html").await, 120 | } 121 | } 122 | 123 | async fn send_response(mut stream: TcpStream, code: u16, reason: &str, filename: &str) { 124 | let contents = fs::read_to_string(filename).await.unwrap(); 125 | let length = contents.len(); 126 | let response = 127 | format!("HTTP/1.1 {code} {reason}\r\nContent-Length: {length}\r\n\r\n{contents}"); 128 | 129 | stream.write_all(response.as_bytes()).await.unwrap(); 130 | } 131 | ``` 132 | 133 | If you want to run this, you'll need the `hello.html` and `404.html` files from [chapter 20][chap20]. 134 | 135 | This looks very similar to our previous single and multithreaded web servers. We have to `use tokio::io::AsyncBufReadExt` to be able to call `buf_reader.lines` in `handle_connection`, because in Tokio `lines` is defined on the `AsyncBufReadExt` trait, and similar for `tokio::io::AsyncWriteExt` and `stream.write_all` in `send_response`. We've also replaced some `for` loops as Rust doesn't (yet) support async for loops. (We also simplified the code for parsing the request, since we weren't actually using any of the headers in our previous examples so we don't bother reading them here.) 136 | 137 | This is also very similar to our single threaded version because if you try reloading the "/sleep" route a few times, you'll see that this is only handling a single request at once. Isn't async supposed to fix that for us? The problem is that in our main loop, we're `await`ing `handle_connection`: 138 | 139 | ```rust 140 | loop { 141 | let (stream, _) = listener.accept().await.unwrap(); 142 | handle_connection(stream).await; 143 | } 144 | ``` 145 | 146 | That `await` will cause the main loop to suspend until `handle_connection` completes. If you're an experienced JavaScript programmer, you might think you can just remove the `await`. This would work in JavaScript, but not in Rust. Rust futures are _lazy_, meaning they won't make any progress if no one is `await`ing them. 147 | 148 | :::info 149 | 150 | If you have a look at the definition of the `Future` trait, you'll see that `Future` has only one method: 151 | 152 | ```rust 153 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll; 154 | ``` 155 | 156 | When we `await` a future, what's happening under the covers is that the runtime will call into `poll` to get the future to make progress. If the future completes, it will return the `Poll::Ready` value. If not, it will return a `Poll::Pending`. When the Future is ready to make progress again, it will call into the `Waker` stored in the `Context` to let the runtime know it should be polled again. 157 | 158 | If you're interested in the internals of `async` and `Future`s in Rust, this is all covered in much greater detail in [Asynchronous Programming in Rust](https://rust-lang.github.io/async-book/). 159 | 160 | ::: 161 | 162 | In order to fix this problem, we have to create this future, then let Tokio know we'd like it to be polled. Tokio's answer to this is something called a `Task`. We can spawn a task with `tokio::spawn`: 163 | 164 | ```rust 165 | loop { 166 | let (stream, _) = listener.accept().await.unwrap(); 167 | tokio::spawn(async move { 168 | handle_connection(stream).await; 169 | }); 170 | } 171 | ``` 172 | 173 | You might have expected `spawn` to take a closure, but it actually takes a future! Here we're using an `async` block to create a future, and the `move` keyword to move ownership of the `stream` into that block. We could also have rewritten this as: 174 | 175 | ```rust 176 | loop { 177 | let (stream, _) = listener.accept().await.unwrap(); 178 | let f = handle_connection(stream); 179 | tokio::spawn(f); 180 | } 181 | ``` 182 | 183 | But the async block is more idiomatic. `spawn` returns a `tokio::task::JoinHandle` similar to the `JoinHandle` we get when you spawn a thread. You can `await` on this handle to wait for the underlying Future to complete. 184 | 185 | Tasks are a form of "green thread". Spawning a task is very lightweight, involving only a single allocation and 64 bytes of memory, so you can easily spawn thousands or millions of tasks (which would be ill-advised if we were talking about OS threads). 186 | 187 | If you've read this far, you've made it to the end of the book. If you enjoyed it, please [star the book on GitHub](https://github.com/jwalton/rust-book-abridged), or [buy me a coffee](https://github.com/sponsors/jwalton). Happy Rusting! 188 | 189 | [chap20]: ./ch20/ch20-01-single-threaded-web-server.md "Chapter 20: Multithreaded Web Server" 190 | -------------------------------------------------------------------------------- /docs/ch19/ch19-01-unsafe.md: -------------------------------------------------------------------------------- 1 | # 19.1 - Unsafe Rust 2 | 3 | :::danger 4 | 5 | This chapter is meant as an introduction to unsafe code, but if you find yourself actually writing unsafe code, it would be a good idea to [read through the Rustonomicon](https://doc.rust-lang.org/nomicon/intro.html). There are many things you can do in unsafe code that will result in undefined behavior, some of which you might surprise you if you're coming from a language like C/C++. 6 | 7 | ::: 8 | 9 | Rust enforces all sort of safety features for us, preventing us from dereferencing null pointers, preventing us from creating potential data races. Sometimes, though, we know better than the compiler. 10 | 11 | Imagine we have a vector with six elements in it. We could create a mutable slice to elements 0-2 and a second mutable slice from elements 3-5, and there'd be no chance of a data race, since these two mutable references point to different regions of memory. The problem with this is that when we call `&mut values[0..=2]`, we have a mutable reference to the underlying array, not to part of the array, and we won't be able to create a second one. Here the compiler is trying to protect us, but it's actually getting in our way. 12 | 13 | _Unsafe_ code in Rust is code where we're allowed to ignore or bypass some of the restrictions Rust places on us, and tell the compiler "Don't worry, I got this." Of course, sometimes we only think we know better than the compiler when in fact what we're actually doing is creating dreaded [undefined behavior](https://doc.rust-lang.org/reference/behavior-considered-undefined.html). So it's not a bad idea to keep unsafe code to a minimum. 14 | 15 | But it's important to note that "unsafe" doesn't necessarily mean incorrect, it's just code that hasn't been inspected by the eagle eye of the Rust compiler. There are plenty of C programs in the world performing useful tasks that are correct (or reasonably correct) and C doesn't even have a borrow checker, so all C code is unsafe as far as a Rust programmer is concerned. 16 | 17 | We can write code inside an unsafe block or inside an unsafe function: 18 | 19 | ```rust 20 | fn main() { 21 | unsafe { 22 | // Do crazy stuff here! 23 | } 24 | } 25 | ``` 26 | 27 | ## Unsafe Superpowers 28 | 29 | There are five _unsafe superpowers_. These are things you're allowed to do inside an `unsafe` block or function that you aren't allowed to do outside of them: 30 | 31 | - Dereference a raw pointer 32 | - Call another unsafe function or method 33 | - Access or modify a mutable static variable 34 | - Implement an unsafe trait 35 | - Access fields of a `union` 36 | 37 | Other than these five things, unsafe code is mostly like safe code. The borrow checker is still checking your borrows, immutable references are still immutable, the sun still rises in the east. These five things do let you get into a surprising amount of trouble though - it's important to document your assumptions and invariants, and carefully ensure you're meeting the assumptions and invariants of any unsafe functions you're calling into. 38 | 39 | ## Dereferencing a Raw Pointer 40 | 41 | Raw pointers come in two types: `*const T` and `*mut T`. These are a lot closer to pointers in C than references in Rust: 42 | 43 | - You can have both immutable and mutable pointers pointing to the same location in memory. 44 | - Pointers can point to memory that has been freed or isn't valid. 45 | - Pointers can be null. 46 | - Pointers don't do any kind of automatic cleanup. 47 | 48 | Since Rust doesn't make any guarantees about raw pointers, it's up to you to make sure you use them correctly by reasoning about your code. Let's see a couple of examples: 49 | 50 | ```rust 51 | let mut num = 5; 52 | 53 | // Create a mutable and const pointer to the same memory. 54 | let r1 = &mut num as *mut i32; 55 | let r2 = unsafe { &*r1 as *const i32 }; 56 | 57 | unsafe { 58 | println!("r1 is: {}", *r1); 59 | println!("r2 is: {}", *r2); 60 | } 61 | 62 | // Create a pointer to a specific address. 63 | // (Hopefully this is memory we own!) 64 | // Note the `as` keyword to cast the value 65 | // into a raw pointer. 66 | let address = 0x012345usize; 67 | let r = address as *const i32; 68 | ``` 69 | 70 | We're allowed to create pointers outside of unsafe code. Creating a pointer never hurt anyone, it's dereferencing a pointer that gets us into trouble, so the dereference is only allowed to happen inside an `unsafe` block. 71 | 72 | Why would you want to use a raw pointer instead of a reference? One case is for calling into C code. Another is when you want to build a "safe" abstraction that the borrow checker won't understand, like our "two mutable slices" example above. We'll see examples of both of these. 73 | 74 | ## Calling an Unsafe Function or Method 75 | 76 | The second of our superpowers is calling an unsafe function or method. If you want to call an unsafe function, you can only do so from an unsafe function or block: 77 | 78 | ```rust 79 | unsafe fn dangerous() {} 80 | 81 | unsafe { 82 | dangerous(); 83 | } 84 | ``` 85 | 86 | Any function that's marked as `unsafe` like this is implicitly an unsafe block. 87 | 88 | ### Creating a Safe Abstraction over Unsafe Code 89 | 90 | Let's go back to our "two mutable slices" example from earlier. We want to write a function that will split a vector into two mutable slices: 91 | 92 | ```rust 93 | let mut v = vec![1, 2, 3, 4, 5, 6]; 94 | 95 | let r = &mut v[..]; 96 | 97 | let (a, b) = r.split_at_mut(3); 98 | 99 | assert_eq!(a, &mut [1, 2, 3]); 100 | assert_eq!(b, &mut [4, 5, 6]); 101 | ``` 102 | 103 | `split_at_mut` is going to call unsafe code, but that doesn't mean that it also has to be unsafe. In fact, the above code works because vector has this method on it already! 104 | 105 | What `split_at_mut` is doing here is creating a "safe abstraction". This is a very common pattern - we hide away the unsafe stuff behind an API that's easy and safe to use. This makes it so we only have to reason about our small API. Here's the implementation of `split_at_mut`: 106 | 107 | ```rust 108 | use std::slice; 109 | 110 | fn split_at_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { 111 | let len = values.len(); 112 | let ptr = values.as_mut_ptr(); 113 | 114 | assert!(mid <= len); 115 | 116 | unsafe { 117 | ( 118 | slice::from_raw_parts_mut(ptr, mid), 119 | slice::from_raw_parts_mut(ptr.add(mid), len - mid), 120 | ) 121 | } 122 | } 123 | ``` 124 | 125 | `slice::from_raw_parts_mut` is unsafe (because it uses a raw pointer to the underlying slice) so we need to call this inside an `unsafe` block. 126 | 127 | ### Using `extern` Functions to Call External Code 128 | 129 | Programming languages can call into code written in other languages via a [_Foreign Function Interface_ (FFI)](https://en.wikipedia.org/wiki/Foreign_function_interface). If you wanted to use OpenSSL from Rust, for example, rather than rewriting OpenSSL in Rust you could just call into the existing C code. You might build a wrapper crate around OpenSSL to turn it into something easy to use from Rust, and provide safe abstractions around everything the library does. 130 | 131 | Here's an example of calling `abs` from the C standard library: 132 | 133 | ```rust 134 | extern "C" { 135 | fn abs(input: i32) -> i32; 136 | } 137 | 138 | fn main() { 139 | unsafe { 140 | println!("Absolute value of -3 according to C: {}", abs(-3)); 141 | } 142 | } 143 | ``` 144 | 145 | The `extern "C"` block tells Rust we're going to call an external function using the C _application binary interface_. 146 | 147 | We can also use the `extern` keyword to create a function that can be called from C: 148 | 149 | ```rust 150 | #[no_mangle] 151 | pub extern "C" fn call_from_c() { 152 | println!("Just called a Rust function from C!"); 153 | } 154 | ``` 155 | 156 | ## Accessing or Modifying a Mutable Static Variable 157 | 158 | As mentioned in [chapter 3][chap3], Rust has global variables, called _static variables_: 159 | 160 | ```rust 161 | static HELLO_WORLD: &str = "Hello, world!"; 162 | 163 | fn main() { 164 | println!("name is: {}", HELLO_WORLD); 165 | } 166 | ``` 167 | 168 | When we use a constant in Rust, the compiler may duplicate the constant in multiple places in memory if they are referenced in multiple places. Static variables, on the other hand, are always guaranteed to occur only once in memory, so no matter where they are referenced in code you'll get back the same instance. Unlike constants, static variables can also be `mut`, but accessing or modifying a mutable static variable is always unsafe: 169 | 170 | ```rust 171 | static mut COUNTER: u32 = 0; 172 | 173 | fn add_to_count(inc: u32) { 174 | unsafe { 175 | COUNTER += inc; 176 | } 177 | } 178 | 179 | fn main() { 180 | add_to_count(3); 181 | 182 | unsafe { 183 | println!("COUNTER: {}", COUNTER); 184 | } 185 | } 186 | ``` 187 | 188 | It is quite difficult, especially in a multi-threaded program, to ensure that access to a mutable static variable doesn't create a data race. 189 | 190 | ## Implementing an Unsafe Trait 191 | 192 | We call a trait an _unsafe trait_ when it has some invariant that the compiler can't verify for us. An example would be the [`Send` and `Sync` traits](../ch16-fearless-concurrency.md#164---extensible-concurrency-with-the-sync-and-send-traits). Any struct made entire of `Send` and `Sync` members automatically becomes `Send` and `Sync`. If we want to create a struct that contains a raw pointer, and we can guarantee that this struct is safe to send across threads or can be accessed from multiple threads, then we'll have to mark the type as `Send` and/or `Sync` ourselves. 193 | 194 | In order to do this, we use an `unsafe impl` block: 195 | 196 | ```rust 197 | unsafe trait Foo { 198 | // methods go here 199 | } 200 | 201 | unsafe impl Foo for i32 { 202 | // method implementations go here 203 | } 204 | ``` 205 | 206 | ## Accessing Fields of a Union 207 | 208 | Unions are included in Rust mainly for calling into C code that uses them. If you want to access a union, it has to be done from an `unsafe` block. 209 | 210 | For the non-C programmers reading this, a `union` is like a `struct`, but each field in the union occupies the same memory. Only one of the fields is ever correct to access at a time, depending on what is stored in the union. This example, for instance, will be four bytes long and holds either a `u32` or an `f32`: 211 | 212 | ```rust 213 | #[repr(C)] 214 | union MyUnion { 215 | f1: u32, 216 | f2: f32, 217 | } 218 | ``` 219 | 220 | Rust has no idea what's stored in this union, and you'll get back a `u32` or an `f32` depending on which one you access, but odds are only one of them contains a meaningful value. You can learn more about unions in [the Rust Reference](https://doc.rust-lang.org/stable/reference/items/unions.html). 221 | 222 | ## Soundness 223 | 224 | This is an example of an unsafe function, taken from [the Rustonomicon](https://doc.rust-lang.org/nomicon/working-with-unsafe.html): 225 | 226 | ```rust 227 | fn index(idx: usize, arr: &[u8]) -> Option { 228 | if idx < arr.len() { 229 | unsafe { 230 | Some(*arr.get_unchecked(idx)) 231 | } 232 | } else { 233 | None 234 | } 235 | } 236 | ``` 237 | 238 | This uses unsafe code, but it checks the bounds of the array before calling into `get_unchecked`, so we can prove this function is _sound_ (it can't cause undefined behavior). Note that if you change the first line of this function to `if idx <= arr.len()` the function becomes unsound, even though we didn't modify any unsafe code! 239 | 240 | ## Verifying unsafe code with Miri 241 | 242 | TODO: Add section here about using [Miri](https://github.com/rust-lang/miri) to test unsafe code. The [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021) has a "Run with Miri" under "Tools" in the upper right corner, which is handy for checking a function. 243 | 244 | [chap3]: ../ch03-common-programming-concepts.md "Chapter 3: Common Programming Concepts" 245 | --------------------------------------------------------------------------------