├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── lesson-plan.md ├── md ├── hint-mutable-borrow-1.md ├── hint-struct-1.md └── index.md ├── src ├── borrow_play.rs ├── defaults.rs ├── entry.rs ├── enums.rs ├── hello_world.rs ├── layering.rs ├── lifetimes_as_part_of_type.rs ├── mutable-borrow.rs ├── named_lifetime_parameters.rs ├── options.rs ├── ownership.rs ├── shared-borrow.rs ├── sharing_and_mutability.rs ├── structs.rs ├── successful_borrowing.rs ├── threads.rs └── traits.rs └── sync-index.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | *~ 7 | 8 | # Executables and generated html 9 | *.exe 10 | *.html 11 | 12 | # Generated by Cargo 13 | /target/ 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ########################################################################### 2 | # CONFIGURATION 3 | 4 | # Directory where the `index.html` will be loaded. Any reference to 5 | # `http://home.url` is changed to this string. 6 | HOME_URL ?= http://rust-tutorials.com/exercises 7 | 8 | # Path to give to `scp` for uploading the tutorials. This is an alias 9 | # that I define locally, so I am pretty confident you will want to 10 | # change it. 11 | UPLOAD_PATH ?= scf:rust-tutorials.com/exercises 12 | 13 | ########################################################################### 14 | # Make rules themselves. 15 | 16 | .PHONY: test html upload 17 | 18 | all: clean html test 19 | 20 | upload: html 21 | scp html/*html $(UPLOAD_PATH) 22 | 23 | clean: 24 | rm -f html/*.html 25 | rm -f src/*.exe 26 | 27 | html: html/index.html html/hint-mutable-borrow-1.html html/hint-struct-1.html 28 | 29 | html/%.html: md/%.md sync-index.py $(wildcard src/*rs) 30 | markdown $< | python sync-index.py "$(HOME_URL)" > $@ 31 | 32 | test: 33 | rustc src/ownership.rs -o src/ownership.exe && src/ownership.exe 34 | rustc --test src/structs.rs -o src/structs.exe && src/structs.exe 35 | rustc --test src/enums.rs -o src/enums.exe && src/enums.exe 36 | rustc --test src/options.rs -o src/options.exe && src/options.exe 37 | rustc src/threads.rs -o src/threads.exe && src/threads.exe 38 | rustc --test src/named_lifetime_parameters.rs -o src/named_lifetime_parameters.exe && src/named_lifetime_parameters.exe 39 | rustc --test src/lifetimes_as_part_of_type.rs -o src/lifetimes_as_part_of_type.exe && src/lifetimes_as_part_of_type.exe 40 | rustc --test src/successful_borrowing.rs -o src/successful_borrowing.exe && src/successful_borrowing.exe 41 | rustc --test src/entry.rs -o src/entry.exe && src/entry.exe 42 | rustc --test src/sharing_and_mutability.rs -o src/sharing_and_mutability.exe && src/sharing_and_mutability.exe 43 | rustc --test src/traits.rs -o src/traits.exe && src/traits.exe 44 | rustc --test src/defaults.rs -o src/defaults.exe && src/defaults.exe 45 | rustc src/layering.rs -o src/layering.exe && src/layering.exe 46 | 47 | 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-tutorializer 2 | A very basic framework for publishing and experimenting with sample code for Rust tutorials. Also includes a collection of lecture plans. Currently for my own personal use, but maybe evolvable into something byeond that. 3 | -------------------------------------------------------------------------------- /lesson-plan.md: -------------------------------------------------------------------------------- 1 | - Hello, World Example: 2 | - Explain: 3 | - `fn` declaration 4 | - `println` is a macro 5 | - Format strings in `println!` 6 | - `println("Hello, {}!", world);` 7 | - also show `{:?}` 8 | - Move "world" into a local variable so we can change it 9 | - `let name = "fellow Rustaceans"; println("Hello, {}!", name);` 10 | - Abstract into a helper fn 11 | - `fn greet(name: String) { println("Hello, {}!", name); }` 12 | - What goes wrong? 13 | - Explain `format!`, show how you can use same helpers 14 | - Explain `push_str` and mutable local variables 15 | - `let mut name = format!("fellow "); name.push_str("Rustacean");` 16 | - Call helper fn twice 17 | - What goes wrong now? 18 | - Timing notes: ~30 minutes from start to here 19 | - Borrowing Example (~5 min): 20 | - Show that `helper(&name)` compiles 21 | - Show that `name.push_str` does not 22 | - Create `rustify(name: &mut String)` that appends some text 23 | - Show that I have to modify `name` to be `let mut` 24 | - Show that I can do `let p = &name; helper(p);` 25 | - Show that I can do `let q = p; helper(p); helper(q);` 26 | - Show that mutable references work differently: 27 | - `{ let m = &mut name; rustify(m); }` is ok 28 | - `{ let m = &mut name; let n = m; rustify(m); }` is not 29 | - `{ let m = &mut name; let n = m; rustify(n); }` is OK again 30 | - Remove braces. Explain that you cannot have a mutable and 31 | immutable reference in scope at the same time. Explain that, for 32 | the moment, compiler does not consider that `m` is not used 33 | after the call to `rustify`, though we are considering changing 34 | that rule. 35 | 36 | 37 | 38 | 39 | - Timing 40 | - Intro: ~5 min 41 | - Hello world: ~5 min (10) 42 | - Ownership slides: ~10 min (20) 43 | - Ownership example: ~10 min (30) 44 | - 3:58 - 4:03 (basic borrow slides: 5min) 45 | - Borrowing: 46 | 47 | -------------------------------------------------------------------------------- /md/hint-mutable-borrow-1.md: -------------------------------------------------------------------------------- 1 | ### Hint for the "Mutable Borrowing" exercise 2 | 3 | You want to change the signature of `join_words` as follows: 4 | 5 | ``` 6 | fn strcat(prefix: &mut String, suffix: &String) { 7 | ... 8 | } 9 | ``` 10 | 11 | Now `prefix` is a mutable reference to some `String` on the caller's 12 | side. We need a mutable reference so we can push onto the string. 13 | 14 | `suffix` is a shared reference. A shared reference suffices because we 15 | only need to read from `suffix`. 16 | 17 | Note that the return value has also changed: since we are going to be 18 | mutating `prefix` in place, we no longer need to return anything. 19 | 20 | [Back to the exercise.](src/mutable-borrow.rs) 21 | -------------------------------------------------------------------------------- /md/hint-struct-1.md: -------------------------------------------------------------------------------- 1 | ### Hint for the "Struct" exercise 2 | 3 | The outline of what you want to do is this: 4 | 5 | - create a mutable variable `sum` that is initially `0.0` 6 | - for each name `name` in the shopping list: 7 | - call the `price()` method to get the (optional) price 8 | - if the price is `Some`, add it to `sum` 9 | - if the price is `None`, return `None` 10 | - return `Some(sum)` 11 | 12 | [Back to the exercise.](src/structs.rs) 13 | -------------------------------------------------------------------------------- /md/index.md: -------------------------------------------------------------------------------- 1 | ## Rust Tutorial 2 | 3 | Thank you for working through these tutorials! I hope you like 4 | them. Before you finish, please let me know what you thought at 5 | [this Google form](http://goo.gl/forms/TTjkyPcF6i) -- your feedback 6 | helps me to improve the tutorials for others. Thanks. --nmatsakis 7 | 8 | FYI: This page is at . 9 | 10 | ### Ownership and Borrowing 11 | 12 | - [Hello World](src/hello_world.rs): 13 | - Time: 3 minutes 14 | - [Ownership](src/ownership.rs): 15 | - Extra credit: Can you do it without copying any data? 16 | - Time: 10 minutes 17 | - [Shared borrows](src/shared-borrow.rs): 18 | - Time: 10 minutes 19 | - [Mutable borrows](src/mutable-borrow.rs): 20 | - [Hint:](hint-mutable-borrow-1.html) Getting the syntax just right can 21 | be a bit tricky if you've never done any Rust 22 | before. [If you need a hint, click here](hint-mutable-borrow-1.html). 23 | - Time: 10 minutes 24 | 25 | ### Structs and such 26 | 27 | - [Structs](src/structs.rs) 28 | - [Hint: Here is an outline of what 29 | the function should do, if you get stuck.](hint-struct-1.html) 30 | - Time: 10 minutes 31 | 32 | ### Traits and Threads 33 | 34 | - [Traits](src/traits.rs) 35 | - [Defaults](src/defaults.rs) 36 | - [Layering](src/layering.rs) 37 | - [Threads](src/threads.rs) 38 | - Extra credit #1: use channels instead 39 | - Extra credit #2: or, instead of channels, use a mutex to compute the best price in 40 | the parallel threads themselves 41 | - Time: 10 minutes 42 | 43 | ### Advanced Lifetimes 44 | 45 | - Ensure you are using **Nightly builds** for the best error messages. 46 | - Exercises are listed in the source code. 47 | 48 | - [Named lifetime parameters](src/named_lifetime_parameters.rs) 49 | - [Lifetimes as part of the type](src/lifetimes_as_part_of_type.rs) 50 | - [Successful borrowing](src/successful_borrowing.rs) 51 | - [Lifetimes in structs](src/entry.rs) 52 | 53 | -------------------------------------------------------------------------------- /src/borrow_play.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let name = format!("fellow Rustaceans"); 3 | helper(&name); 4 | helper(&name); 5 | } 6 | 7 | fn helper(name: &String) { 8 | println!("Hello, {}!", name); 9 | } 10 | -------------------------------------------------------------------------------- /src/defaults.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | // The trait 5 | //////////////////////////////////////////////////////////////////////////////// 6 | 7 | trait Price { 8 | fn price(&self, item_name: &str) -> Option; 9 | 10 | fn total_price(&self, shopping_list: &[&str]) -> Option { 11 | // Goal: compute the total price of all items in the shopping 12 | // list. If any of the options are not present, return `None`. 13 | // PROMPT None 14 | // START SOLUTION 15 | let mut sum = 0.0; 16 | for name in shopping_list { 17 | match self.price(name) { 18 | Some(v) => sum += v, 19 | None => return None 20 | } 21 | } 22 | Some(sum) 23 | // END SOLUTION 24 | } 25 | } 26 | 27 | //////////////////////////////////////////////////////////////////////////////// 28 | // Store 29 | //////////////////////////////////////////////////////////////////////////////// 30 | 31 | struct Store { 32 | name: String, 33 | items: Vec, 34 | } 35 | 36 | #[derive(Debug)] 37 | struct Item { 38 | name: &'static str, 39 | price: f32, 40 | } 41 | 42 | impl Store { 43 | fn new(name: String) -> Store { 44 | Store { 45 | name: name, 46 | items: vec![], 47 | } 48 | } 49 | 50 | fn add_item(&mut self, item: Item) { 51 | self.items.push(item); 52 | } 53 | } 54 | 55 | impl Price for Store { 56 | fn price(&self, item_name: &str) -> Option { 57 | for item in &self.items { 58 | if item.name == item_name { 59 | return Some(item.price); 60 | } 61 | } 62 | None 63 | } 64 | } 65 | 66 | fn build_store() -> Store { 67 | let mut store = Store::new(format!("Rustmart")); 68 | store.add_item(Item { name: "chocolate", price: 5.0 }); 69 | store.add_item(Item { name: "socks", price: 23.0 }); 70 | store.add_item(Item { name: "plush Mozilla dinosaur", price: 13.0 }); 71 | store 72 | } 73 | 74 | //////////////////////////////////////////////////////////////////////////////// 75 | // Factory 76 | //////////////////////////////////////////////////////////////////////////////// 77 | 78 | // A factory for just a single kind of item 79 | struct Factory { 80 | item_name: &'static str, 81 | wholesale_price: f32, 82 | } 83 | 84 | impl Price for Factory { 85 | fn price(&self, item_name: &str) -> Option { 86 | if self.item_name == item_name { 87 | Some(self.wholesale_price) 88 | } else { 89 | None 90 | } 91 | } 92 | } 93 | 94 | fn build_factory() -> Factory { 95 | Factory { 96 | item_name: "sprocket", 97 | wholesale_price: 7.67, 98 | } 99 | } 100 | 101 | //////////////////////////////////////////////////////////////////////////////// 102 | // Tests 103 | //////////////////////////////////////////////////////////////////////////////// 104 | 105 | #[test] 106 | fn total_price_store() { 107 | let store = build_store(); 108 | let list = vec!["chocolate", "plush Mozilla dinosaur"]; 109 | assert_eq!(store.total_price(&list), Some(18.0)); 110 | } 111 | 112 | #[test] 113 | fn total_price_missing_store() { 114 | let store = build_store(); 115 | let list = vec!["chocolate", "plush Mozilla dinosaur", "fork and knife"]; 116 | assert_eq!(store.total_price(&list), None); 117 | } 118 | 119 | #[test] 120 | fn total_price_factory() { 121 | let factory = build_factory(); 122 | let list = vec!["sprocket"]; 123 | assert_eq!(factory.total_price(&list), Some(7.67)); 124 | } 125 | 126 | #[test] 127 | fn total_price_missing_factory() { 128 | let factory = build_factory(); 129 | let list = vec!["sprocket", "socks"]; 130 | assert_eq!(factory.total_price(&list), None); 131 | } 132 | -------------------------------------------------------------------------------- /src/entry.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | 3 | // **Exercise 1.** Consider a method `fn remove(self) -> V` that 4 | // removes a value from an entry: 5 | // - Which of the `Entry` types does this method belong on? 6 | // - Implement the method. 7 | // 8 | // **Exercise 2.** The entry types represent a limited sort of state 9 | // machine that does not allow very many transitions. You could also 10 | // permit going back and forth between the empty and full states. 11 | // Modify the methods `insert` and `remove` to have the following 12 | // type signatures: 13 | // 14 | // - `fn insert(self) -> FoundEntry<'map, K, V>` 15 | // - `fn remove(self) -> (NotFoundEntry<'map, K, V>, V)` 16 | // 17 | // You can uncomment the test `exercise_2` to see how they should be 18 | // used. 19 | 20 | pub struct Map { 21 | elements: Vec<(K, V)>, 22 | } 23 | 24 | impl Map { 25 | pub fn new() -> Self { 26 | Map { elements: vec![] } 27 | } 28 | 29 | pub fn insert(&mut self, key: K, value: V) { 30 | for pair in &mut self.elements { 31 | if pair.0 == key { 32 | pair.1 = value; 33 | return; 34 | } 35 | } 36 | self.elements.push((key, value)); 37 | } 38 | 39 | pub fn get(&self, key: &K) -> Option<&V> { 40 | self.elements.iter().rev().find(|pair| pair.0 == *key).map(|pair| &pair.1) 41 | } 42 | 43 | pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { 44 | self.elements.iter_mut().rev().find(|pair| pair.0 == *key).map(|pair| &mut pair.1) 45 | } 46 | 47 | fn index_of(&self, key: &K) -> Option { 48 | self.elements.iter().position(|pair| pair.0 == *key) 49 | } 50 | 51 | pub fn entry<'map>(&'map mut self, key: K) -> Entry<'map, K, V> { 52 | match self.index_of(&key) { 53 | Some(position) => { 54 | Entry::Found(FoundEntry { 55 | index: position, 56 | elements: &mut self.elements, 57 | }) 58 | } 59 | None => { 60 | Entry::NotFound(NotFoundEntry { 61 | key: key, 62 | elements: &mut self.elements, 63 | }) 64 | } 65 | } 66 | } 67 | } 68 | 69 | pub enum Entry<'map, K, V> 70 | where K: Eq, 71 | K: 'map, 72 | V: 'map 73 | { 74 | Found(FoundEntry<'map, K, V>), 75 | NotFound(NotFoundEntry<'map, K, V>), 76 | } 77 | 78 | pub struct FoundEntry<'map, K, V> 79 | where K: Eq, 80 | K: 'map, 81 | V: 'map 82 | { 83 | index: usize, 84 | elements: &'map mut Vec<(K, V)>, 85 | } 86 | 87 | pub struct NotFoundEntry<'map, K, V> 88 | where K: Eq, 89 | K: 'map, 90 | V: 'map 91 | { 92 | key: K, 93 | elements: &'map mut Vec<(K, V)>, 94 | } 95 | 96 | impl<'map, K: Eq, V> Entry<'map, K, V> { 97 | fn or_insert(self, value: V) -> &'map mut V { 98 | match self { 99 | Entry::Found(mut data) => &mut data.elements[data.index].1, 100 | Entry::NotFound(mut data) => { 101 | data.elements.push((data.key, value)); 102 | &mut data.elements.last_mut().unwrap().1 103 | } 104 | } 105 | } 106 | } 107 | 108 | impl<'map, K: Eq, V> FoundEntry<'map, K, V> { 109 | fn get(self) -> &'map mut V { 110 | &mut self.elements[self.index].1 111 | } 112 | 113 | fn remove(self) -> V { 114 | // PROMPT unimplemented!() 115 | // START SOLUTION 116 | self.elements.swap_remove(self.index).1 117 | // END SOLUTION 118 | } 119 | } 120 | 121 | impl<'map, K: Eq, V> NotFoundEntry<'map, K, V> { 122 | fn insert(self, value: V) -> &'map mut V { 123 | self.elements.push((self.key, value)); 124 | &mut self.elements.last_mut().unwrap().1 125 | } 126 | } 127 | 128 | #[test] 129 | fn or_insert() { 130 | let mut map = Map::new(); 131 | map.insert('a', format!("alpha")); 132 | assert_eq!(map.entry('a').or_insert(format!("beta")), &format!("alpha")); 133 | } 134 | 135 | #[test] 136 | fn exercise_1() { 137 | let mut map = Map::new(); 138 | map.insert('a', format!("alpha")); 139 | let data = match map.entry('a') { 140 | Entry::Found(data) => data.remove(), 141 | Entry::NotFound(_) => panic!(), 142 | }; 143 | assert_eq!(data, format!("alpha")); 144 | } 145 | 146 | //#[test] 147 | //fn exercise_2() { 148 | // let mut map = Map::new(); 149 | // map.insert('a', format!("alpha")); 150 | // 151 | // { 152 | // let not_found = match map.entry('a') { 153 | // Entry::NotFound(data) => data, 154 | // Entry::Found(data) => data.remove().0, 155 | // }; 156 | // not_found.insert(format!("beta")); 157 | // } 158 | // 159 | // assert_eq!(map.get(&'a'), Some(&format!("beta"))); 160 | //} 161 | -------------------------------------------------------------------------------- /src/enums.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[derive(Debug)] 4 | struct Store { 5 | name: String, 6 | items: Vec, 7 | } 8 | 9 | #[derive(Debug)] 10 | struct Item { 11 | name: &'static str, 12 | price: f32, 13 | tax_rate: TaxRate, 14 | } 15 | 16 | #[derive(Debug)] 17 | enum TaxRate { 18 | TaxExempt, 19 | SalesTax(f32), 20 | } 21 | 22 | impl Store { 23 | fn new(name: String) -> Store { 24 | Store { 25 | name: name, 26 | items: vec![], 27 | } 28 | } 29 | 30 | fn add_item(&mut self, item: Item) { 31 | self.items.push(item); 32 | } 33 | 34 | fn price(&self, item_name: &str) -> f32 { 35 | for item in &self.items { 36 | if item.name == item_name { 37 | let base_price = item.price; 38 | // Goal 1. Adjust for `item.tax_rate` 39 | // 40 | // Goal 2. Once you have things working, try removing 41 | // one of the arms in your `match` expression and 42 | // see what happens. 43 | 44 | // PROMPT return base_price; 45 | // START SOLUTION 46 | return match item.tax_rate { 47 | TaxRate::TaxExempt => 48 | base_price, 49 | TaxRate::SalesTax(percent) => 50 | base_price + base_price * percent, 51 | }; 52 | // END SOLUTION 53 | } 54 | } 55 | 56 | panic!("no such item {:?}", item_name); 57 | } 58 | 59 | fn total_price(&self, shopping_list: &[&str]) -> f32 { 60 | shopping_list.iter() 61 | .map(|name| self.price(name)) 62 | .fold(0.0, |a, b| a + b) 63 | } 64 | } 65 | 66 | fn build_store() -> Store { 67 | use self::TaxRate::*; 68 | 69 | let mut store = Store::new(format!("Rustmart")); 70 | store.add_item(Item { 71 | name: "chocolate", 72 | price: 5.0, 73 | tax_rate: TaxExempt, 74 | }); 75 | store.add_item(Item { 76 | name: "socks", 77 | price: 23.0, 78 | tax_rate: SalesTax(0.05), 79 | }); 80 | store.add_item(Item { 81 | name: "plush Mozilla dinosaur", 82 | price: 13.0, 83 | tax_rate: SalesTax(0.05), 84 | }); 85 | store 86 | } 87 | 88 | #[test] 89 | fn total_price() { 90 | let store = build_store(); 91 | let list = vec!["chocolate", "plush Mozilla dinosaur"]; 92 | assert_eq!(store.total_price(&list), 18.65); 93 | } 94 | 95 | -------------------------------------------------------------------------------- /src/hello_world.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /src/layering.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | // A trait for printing data with indentation 4 | trait Print { 5 | // A default method for ignoring indentation 6 | fn print(&self) { 7 | self.print_with_indent(0); 8 | } 9 | 10 | // Print the data with the given indentation level 11 | fn print_with_indent(&self, indent: usize); 12 | } 13 | 14 | // A helper function that produces `amount` indentation 15 | fn print_indent(amount: usize) { 16 | print!("{:width$}","", width = amount); 17 | } 18 | 19 | impl Print for u64 { 20 | fn print_with_indent(&self, indent: usize) { 21 | print_indent(indent); 22 | println!("{}", self) 23 | } 24 | } 25 | 26 | impl Print for char { 27 | fn print_with_indent(&self, indent: usize) { 28 | // Goal: print the given character at the given indentation level, 29 | // surrounding the character by single quotes 30 | // START SOLUTION 31 | print_indent(indent); 32 | println!("'{}'", self); 33 | // END SOLUTION 34 | } 35 | } 36 | 37 | impl Print for Vec { 38 | fn print_with_indent(&self, indent: usize) { 39 | // Goal: print the contents of the vector, indenting items by two 40 | // additional characters, and surrounding the vector by `Vec [` and `]`. 41 | // PROMPT for item in self {} 42 | // START SOLUTION 43 | print_indent(indent); 44 | println!("Vec ["); 45 | 46 | for item in self { 47 | item.print_with_indent(indent + 2) 48 | } 49 | 50 | print_indent(indent); 51 | println!("]"); 52 | // END SOLUTION 53 | } 54 | } 55 | 56 | fn main() { 57 | /* Make this compile and print: 58 | * 59 | * Vec [ 60 | * 'h' 61 | * 'i' 62 | * ] 63 | * 64 | */ 65 | 66 | let w: Vec = vec!['h', 'i']; 67 | w.print(); 68 | 69 | /* Make this compile and print: 70 | * 71 | * Vec [ 72 | * Vec [ 73 | * 2 74 | * ] 75 | * Vec [ 76 | * 3 77 | * 5 78 | * ] 79 | * ] 80 | * 81 | */ 82 | 83 | let mut v: Vec> = vec![]; 84 | v.push(vec![2]); 85 | v.push(vec![3, 5]); 86 | v.print(); 87 | } 88 | -------------------------------------------------------------------------------- /src/lifetimes_as_part_of_type.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | 3 | // **Exercise 1.** For the method `get`, identify at least 4 lifetimes 4 | // that must be inferred. 5 | // 6 | // **Exercise 2.** Modify the signature of `get` such that the method 7 | // `get` fails to compile with a lifetime inference error. 8 | // 9 | // **Exercise 3.** Modify the signature of `get` such that the 10 | // `do_not_compile` test fails to compile with a lifetime error 11 | // (but `get` does not have any errors). 12 | // 13 | // **Exercise 4.** There are actually two ways to achieve Exercise 3. 14 | // Can you find the other one? 15 | 16 | pub struct Map { 17 | elements: Vec<(K, V)>, 18 | } 19 | 20 | impl Map { 21 | pub fn new() -> Self { 22 | Map { elements: vec![] } 23 | } 24 | 25 | pub fn get(&self, key: &K) -> Option<&V> { 26 | let matching_pair: Option<&(K, V)> = <[_]>::iter(&self.elements) 27 | .rev() 28 | .find(|pair| pair.0 == *key); 29 | matching_pair.map(|pair| &pair.1) 30 | } 31 | } 32 | 33 | #[test] 34 | // START SOLUTION 35 | #[should_panic] 36 | // END SOLUTION 37 | fn do_not_compile() { 38 | let map: Map = Map::new(); 39 | let r; 40 | let key = &'c'; 41 | r = map.get(key); 42 | panic!("If this test is running, your program compiled, and that's bad!"); 43 | } 44 | -------------------------------------------------------------------------------- /src/mutable-borrow.rs: -------------------------------------------------------------------------------- 1 | pub fn main() { 2 | let (mut str1, str) = two_words(); 3 | str1 = join_words(str1, str2); 4 | println!("concatenated string is {:?}", str1); 5 | } 6 | 7 | fn two_words() -> (String, String) { 8 | (format!("fellow"), format!("Rustaceans")) 9 | } 10 | 11 | /// Concatenate `suffix` onto the end of `prefix`. 12 | fn join_words(mut prefix: String, suffix: String) -> String { 13 | prefix.push(' '); // separate the words with a space 14 | for ch in suffix.chars() { 15 | prefix.push(ch); 16 | } 17 | prefix 18 | } 19 | 20 | // Challenge: Convert `join_words` to use borrowing, not ownership. 21 | // The new function should mutate `prefix` in place, and should not 22 | // take ownership of `suffix`. 23 | // 24 | // Hint: If you'd like a hint as to how to proceed, open 25 | // . 26 | 27 | // Question: Now that you've converted `strcat`, what happens if you 28 | // call `strcat` using the same string for `prefix` and `suffix`? 29 | // Why? 30 | -------------------------------------------------------------------------------- /src/named_lifetime_parameters.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | 3 | // **Exercise 1.** Modify the signature of `get` to use an explicit 4 | // lifetime name in the return type. 5 | // 6 | // **Exercise 2.** Change the signature of `get` to: 7 | // 8 | // fn get<'a>(&'a mut self, key: &'a K) -> Option<&'a V> 9 | // 10 | // - Which test fails to compile? 11 | // - Can you explain why? 12 | // 13 | // **Exercise 3.** Rewrite `insert` to not just push. 14 | 15 | pub struct Map { 16 | elements: Vec<(K, V)>, 17 | } 18 | 19 | impl Map { 20 | pub fn new() -> Self { 21 | Map { elements: vec![] } 22 | } 23 | 24 | pub fn insert(&mut self, key: K, value: V) { 25 | self.elements.push((key, value)); 26 | } 27 | 28 | pub fn get(&self, key: &K) -> Option<&V> { 29 | self.elements.iter().rev().find(|pair| pair.0 == *key).map(|pair| &pair.1) 30 | } 31 | } 32 | 33 | #[test] 34 | fn basic() { 35 | let mut map = Map::new(); 36 | 37 | map.insert('a', format!("alpha")); 38 | map.insert('b', format!("beta")); 39 | map.insert('g', format!("gamma")); 40 | 41 | assert_eq!(map.get(&'a'), Some(&format!("alpha"))); 42 | assert_eq!(map.get(&'b'), Some(&format!("beta"))); 43 | assert_eq!(map.get(&'g'), Some(&format!("gamma"))); 44 | 45 | map.insert('a', format!("xxx")); 46 | 47 | assert_eq!(map.get(&'a'), Some(&format!("xxx"))); 48 | assert_eq!(map.get(&'b'), Some(&format!("beta"))); 49 | assert_eq!(map.get(&'g'), Some(&format!("gamma"))); 50 | } 51 | 52 | #[test] 53 | fn lock_receiver() { 54 | let mut map = Map::new(); 55 | let mut string = format!("alpha"); 56 | map.insert('a', string.clone()); 57 | let r = map.get(&'a'); 58 | assert_eq!(r, Some(&string)); 59 | } 60 | -------------------------------------------------------------------------------- /src/options.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[derive(Debug)] 4 | struct Store { 5 | name: String, 6 | items: Vec, 7 | } 8 | 9 | #[derive(Debug)] 10 | struct Item { 11 | name: &'static str, 12 | price: f32, 13 | tax_rate: TaxRate, 14 | } 15 | 16 | #[derive(Debug)] 17 | enum TaxRate { 18 | TaxExempt, 19 | SalesTax(f32), 20 | } 21 | 22 | impl Store { 23 | fn new(name: String) -> Store { 24 | Store { 25 | name: name, 26 | items: vec![], 27 | } 28 | } 29 | 30 | fn add_item(&mut self, item: Item) { 31 | self.items.push(item); 32 | } 33 | 34 | fn price(&self, item_name: &str) -> Option { 35 | for item in &self.items { 36 | if item.name == item_name { 37 | let base_price = item.price; 38 | let adjusted_price = match item.tax_rate { 39 | TaxRate::TaxExempt => 40 | base_price, 41 | TaxRate::SalesTax(percent) => 42 | base_price + base_price * percent, 43 | }; 44 | return Some(adjusted_price); 45 | } 46 | } 47 | 48 | None 49 | } 50 | 51 | fn total_price(&self, shopping_list: &[&str]) -> Option { 52 | let mut sum = 0.0; 53 | // PROMPT for name in shopping_list { 54 | // PROMPT sum += self.price(name); 55 | // PROMPT } 56 | // PROMPT sum 57 | // START SOLUTION 58 | for name in shopping_list { 59 | sum += match self.price(name) { 60 | Some(s) => s, 61 | None => return None 62 | }; 63 | } 64 | Some(sum) 65 | // END SOLUTION 66 | } 67 | } 68 | 69 | fn build_store() -> Store { 70 | use self::TaxRate::*; 71 | 72 | let mut store = Store::new(format!("Rustmart")); 73 | store.add_item(Item { 74 | name: "chocolate", 75 | price: 5.0, 76 | tax_rate: TaxExempt, 77 | }); 78 | store.add_item(Item { 79 | name: "socks", 80 | price: 23.0, 81 | tax_rate: SalesTax(0.05), 82 | }); 83 | store.add_item(Item { 84 | name: "plush Mozilla dinosaur", 85 | price: 13.0, 86 | tax_rate: SalesTax(0.05), 87 | }); 88 | store 89 | } 90 | 91 | #[test] 92 | fn total_price() { 93 | let store = build_store(); 94 | let list = vec!["chocolate", "plush Mozilla dinosaur"]; 95 | assert_eq!(store.total_price(&list), Some(18.65)); 96 | } 97 | 98 | #[test] 99 | fn missing_item() { 100 | let store = build_store(); 101 | let list = vec!["chocolate", "plush Mozilla dinosaur", "milk"]; 102 | assert_eq!(store.total_price(&list), None); 103 | } 104 | 105 | -------------------------------------------------------------------------------- /src/ownership.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let (adjective, name) = two_words(); 3 | let name = format!("{} {}", adjective, name); 4 | print_out(name); 5 | } 6 | 7 | fn two_words() -> (String, String) { 8 | (format!("fellow"), format!("Rustaceans")) 9 | } 10 | 11 | fn remove_vowels(name: String) -> String { 12 | // Goal #1: What is needed here to make this compile? 13 | // PROMPT let output = String::new(); 14 | // START SOLUTION 15 | let mut output = String::new(); 16 | // END SOLUTION 17 | for c in name.chars() { 18 | match c { 19 | 'a' | 'e' | 'i' | 'o' | 'u' => { 20 | // skip vowels 21 | } 22 | _ => { 23 | output.push(c); 24 | } 25 | } 26 | } 27 | output 28 | } 29 | 30 | fn print_out(name: String) { 31 | let devowelized_name = remove_vowels(name); 32 | println!("Removing vowels yields {:?}", devowelized_name); 33 | 34 | // Goal #2: What happens when you uncomment the `println` below? 35 | // Can you change the code above so that the code below compiles 36 | // successfully? 37 | // 38 | // println!("Removing vowels from {:?} yields {:?}", 39 | // name, devowelized_name); 40 | 41 | // Extra credit: Can you do it without copying any data? 42 | // (Using only ownership transfer) 43 | } 44 | -------------------------------------------------------------------------------- /src/shared-borrow.rs: -------------------------------------------------------------------------------- 1 | pub fn main() { 2 | let string = format!("my friend"); 3 | greet(string.clone()); 4 | greet(string); 5 | } 6 | 7 | fn greet(name: String) { 8 | println!("Hello, {}!", name); 9 | } 10 | 11 | // Goal #1: Convert `greet` to use borrowing, not ownership, so that 12 | // this program executes without doing any cloning. 13 | // 14 | // Goal #2: Use a subslice so that it prints "Hello, friend" instead of 15 | // "Hello, my friend". 16 | -------------------------------------------------------------------------------- /src/sharing_and_mutability.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | 3 | // Exercise #1. Make the `basic_ref_cell` test pass. 4 | // 5 | // Exercise #2. Modify the body of `fill_vec` (not its signature) 6 | // so as to make the `ref_cell_ref` test pass. 7 | 8 | #[test] 9 | fn basic_ref_cell() { 10 | let data = RefCell::new(vec![]); 11 | 12 | let mut p = data.borrow_mut(); 13 | p.push("Hello,"); 14 | p.push("World!"); 15 | 16 | // START SOLUTION 17 | ::std::mem::drop(p); 18 | // END SOLUTION 19 | 20 | assert_eq!(&data.borrow()[..], &["Hello,", "World!"]); 21 | } 22 | 23 | fn push_twice(f: F, g: G) 24 | where F: FnOnce(&'static str), 25 | G: FnOnce(&'static str) 26 | { 27 | f("Hello,"); 28 | g("World!"); 29 | } 30 | 31 | fn fill_vec(vec: &mut Vec<&'static str>) { 32 | // This won't compile, but this captures what we *want* 33 | // to do. Invoke `push_twice` and, for each callback, 34 | // push the resulting data onto `vec`: 35 | // 36 | // push_twice(|x| vec.push(x), 37 | // |y| vec.push(y)); 38 | 39 | // PROMPT unimplemented!() 40 | // START SOLUTION 41 | let cell = RefCell::new(vec); 42 | push_twice(|x| cell.borrow_mut().push(x), 43 | |y| cell.borrow_mut().push(y)); 44 | // END SOLUTION 45 | } 46 | 47 | #[test] 48 | fn ref_cell_ref() { 49 | let mut data = vec![]; 50 | fill_vec(&mut data); 51 | assert_eq!(&data[..], &["Hello,", "World!"]); 52 | } 53 | -------------------------------------------------------------------------------- /src/structs.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | struct Store { 4 | name: String, 5 | items: Vec, 6 | } 7 | 8 | #[derive(Debug)] 9 | struct Item { 10 | name: String, 11 | price: f32, 12 | } 13 | 14 | impl Store { 15 | fn new(name: String) -> Store { 16 | Store { 17 | name: name, 18 | items: vec![], 19 | } 20 | } 21 | 22 | fn add_item(&mut self, item: Item) { 23 | self.items.push(item); 24 | } 25 | 26 | fn price(&self, item_name: &str) -> Option { 27 | for item in &self.items { 28 | if item.name == item_name { 29 | return Some(item.price); 30 | } 31 | } 32 | None 33 | } 34 | 35 | fn total_price(&self, shopping_list: &[&str]) -> Option { 36 | // Goal: compute the total price of all items in the shopping 37 | // list. If any of the options are not present, return `None`. 38 | // 39 | // Hint: If you'd like a hint as to how to proceed, open 40 | // . 41 | 42 | // PROMPT Some(0.0) 43 | // START SOLUTION 44 | let mut sum = 0.0; 45 | for name in shopping_list { 46 | match self.price(name) { 47 | Some(v) => sum += v, 48 | None => return None 49 | } 50 | } 51 | Some(sum) 52 | // END SOLUTION 53 | } 54 | } 55 | 56 | fn build_store() -> Store { 57 | let mut store = Store::new(format!("Rustmart")); 58 | store.add_item(Item { name: format!("chocolate"), price: 5.0 }); 59 | store.add_item(Item { name: format!("socks"), price: 23.0 }); 60 | store.add_item(Item { name: format!("plush Mozilla dinosaur"), price: 13.0 }); 61 | store 62 | } 63 | 64 | #[test] 65 | fn total_price() { 66 | let store = build_store(); 67 | let list = vec!["chocolate", "plush Mozilla dinosaur"]; 68 | assert_eq!(store.total_price(&list), Some(18.0)); 69 | } 70 | 71 | #[test] 72 | fn total_price_missing() { 73 | let store = build_store(); 74 | let list = vec!["chocolate", "plush Mozilla dinosaur", "fork and knife"]; 75 | assert_eq!(store.total_price(&list), None); 76 | } 77 | 78 | -------------------------------------------------------------------------------- /src/successful_borrowing.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | 3 | pub struct Map { 4 | elements: Vec<(K, V)>, 5 | } 6 | 7 | impl Map { 8 | pub fn new() -> Self { 9 | Map { elements: vec![] } 10 | } 11 | 12 | pub fn insert(&mut self, key: K, value: V) { 13 | self.elements.push((key, value)); 14 | } 15 | 16 | pub fn get(&self, key: &K) -> Option<&V> { 17 | self.elements.iter().rev().find(|pair| pair.0 == *key).map(|pair| &pair.1) 18 | } 19 | 20 | pub fn remove(&mut self, key: &K) { 21 | for (index, pair) in self.elements.iter().enumerate() { 22 | if pair.0 == *key { 23 | // PROMPT self.elements.remove(index); 24 | // PROMPT return; 25 | } 26 | } 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/threads.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::f32::INFINITY; 4 | use std::sync::Arc; 5 | use std::thread; 6 | 7 | struct Store { 8 | name: String, 9 | items: Vec, 10 | } 11 | 12 | #[derive(Debug)] 13 | struct Item { 14 | name: &'static str, 15 | price: f32, 16 | } 17 | 18 | impl Store { 19 | fn new(name: String) -> Store { 20 | Store { 21 | name: name, 22 | items: vec![], 23 | } 24 | } 25 | 26 | fn add_item(&mut self, item: Item) { 27 | self.items.push(item); 28 | } 29 | 30 | fn price(&self, item_name: &str) -> f32 { 31 | for item in &self.items { 32 | if item.name == item_name { 33 | return item.price; 34 | } 35 | } 36 | 37 | panic!("no such item {:?}", item_name); 38 | } 39 | 40 | fn total_price(&self, shopping_list: &[&str]) -> f32 { 41 | shopping_list.iter() 42 | .map(|name| self.price(name)) 43 | .fold(0.0, |a, b| a + b) 44 | } 45 | } 46 | 47 | fn build_stores() -> Vec { 48 | let mut stores = vec![]; 49 | 50 | let mut store = Store::new(format!("Rustmart")); 51 | store.add_item(Item { name: "chocolate", price: 5.0 }); 52 | store.add_item(Item { name: "socks", price: 23.0 }); 53 | store.add_item(Item { name: "plush Mozilla dinosaur", price: 13.0 }); 54 | stores.push(store); 55 | 56 | let mut store = Store::new(format!("Rarget")); 57 | store.add_item(Item { name: "chocolate", price: 2.5 }); 58 | store.add_item(Item { name: "socks", price: 20.0 }); 59 | store.add_item(Item { name: "plush Mozilla dinosaur", price: 20.0 }); 60 | stores.push(store); 61 | 62 | stores 63 | } 64 | 65 | fn main() { 66 | let stores = build_stores(); 67 | 68 | let shopping_list = vec!["chocolate", "plush Mozilla dinosaur"]; 69 | let shopping_list = Arc::new(shopping_list); 70 | 71 | let mut handles = vec![]; 72 | for store in stores { 73 | let shopping_list = shopping_list.clone(); 74 | handles.push(thread::spawn(move || { 75 | let sum = store.total_price(&shopping_list); 76 | (store.name, sum) 77 | })); 78 | } 79 | 80 | let mut best = None; 81 | let mut best_price = INFINITY; 82 | 83 | // Goal: join the threads here! 84 | // Extra credit: rewrite to use channels or mutexes. 85 | // START SOLUTION 86 | for handle in handles { 87 | let (name, sum) = handle.join().unwrap(); 88 | println!("At {}, I would spend ${}.", name, sum); 89 | if sum < best_price { 90 | best = Some(name); 91 | best_price = sum; 92 | } 93 | } 94 | // END SOLUTION 95 | 96 | println!("--> Go to {}!", best.unwrap()); 97 | } 98 | 99 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | // The trait 5 | //////////////////////////////////////////////////////////////////////////////// 6 | 7 | trait Price { 8 | fn price(&self, item_name: &str) -> Option; 9 | } 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | // Store 13 | //////////////////////////////////////////////////////////////////////////////// 14 | 15 | struct Store { 16 | name: String, 17 | items: Vec, 18 | } 19 | 20 | #[derive(Debug)] 21 | struct Item { 22 | name: &'static str, 23 | price: f32, 24 | } 25 | 26 | impl Store { 27 | fn new(name: String) -> Store { 28 | Store { 29 | name: name, 30 | items: Vec::new(), 31 | } 32 | } 33 | 34 | fn add_item(&mut self, item: Item) { 35 | self.items.push(item); 36 | } 37 | } 38 | 39 | impl Price for Store { 40 | fn price(&self, item_name: &str) -> Option { 41 | for item in &self.items { 42 | if item.name == item_name { 43 | return Some(item.price); 44 | } 45 | } 46 | None 47 | } 48 | } 49 | 50 | fn build_store() -> Store { 51 | let mut store = Store::new(format!("Rustmart")); 52 | store.add_item(Item { name: "chocolate", price: 5.0 }); 53 | store.add_item(Item { name: "socks", price: 23.0 }); 54 | store.add_item(Item { name: "plush Mozilla dinosaur", price: 13.0 }); 55 | store 56 | } 57 | 58 | //////////////////////////////////////////////////////////////////////////////// 59 | // Factory 60 | //////////////////////////////////////////////////////////////////////////////// 61 | 62 | // A factory for just a single kind of item 63 | struct Factory { 64 | item_name: &'static str, 65 | wholesale_price: f32, 66 | } 67 | 68 | impl Price for Factory { 69 | fn price(&self, item_name: &str) -> Option { 70 | // Goal: return the price of the factory's item, if the name matches; 71 | // otherwise return `None`. 72 | // PROMPT None 73 | // START SOLUTION 74 | if self.item_name == item_name { 75 | Some(self.wholesale_price) 76 | } else { 77 | None 78 | } 79 | // END SOLUTION 80 | } 81 | } 82 | 83 | fn build_factory() -> Factory { 84 | Factory { 85 | item_name: "sprocket", 86 | wholesale_price: 7.67, 87 | } 88 | } 89 | 90 | //////////////////////////////////////////////////////////////////////////////// 91 | // Total price 92 | //////////////////////////////////////////////////////////////////////////////// 93 | 94 | fn total_price(provider: &P, shopping_list: &[&str]) -> Option { 95 | // Goal: compute the total price of all items in the shopping 96 | // list. If any of the options are not present, return `None`. 97 | // PROMPT None 98 | // START SOLUTION 99 | let mut sum = 0.0; 100 | for name in shopping_list { 101 | match provider.price(name) { 102 | Some(v) => sum += v, 103 | None => return None 104 | } 105 | } 106 | Some(sum) 107 | // END SOLUTION 108 | } 109 | 110 | //////////////////////////////////////////////////////////////////////////////// 111 | // Tests 112 | //////////////////////////////////////////////////////////////////////////////// 113 | 114 | #[test] 115 | fn total_price_store() { 116 | let store = build_store(); 117 | let list = vec!["chocolate", "plush Mozilla dinosaur"]; 118 | assert_eq!(total_price(&store, &list), Some(18.0)); 119 | } 120 | 121 | #[test] 122 | fn total_price_missing_store() { 123 | let store = build_store(); 124 | let list = vec!["chocolate", "plush Mozilla dinosaur", "fork and knife"]; 125 | assert_eq!(total_price(&store, &list), None); 126 | } 127 | 128 | #[test] 129 | fn total_price_factory() { 130 | let factory = build_factory(); 131 | let list = vec!["sprocket"]; 132 | assert_eq!(total_price(&factory, &list), Some(7.67)); 133 | } 134 | 135 | #[test] 136 | fn total_price_missing_factory() { 137 | let factory = build_factory(); 138 | let list = vec!["sprocket", "socks"]; 139 | assert_eq!(total_price(&factory, &list), None); 140 | } 141 | -------------------------------------------------------------------------------- /sync-index.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import re 5 | from urllib import urlencode 6 | 7 | href = re.compile(r'href="src/([-a-z_]+).rs"') 8 | start_solution = re.compile(r' *// START SOLUTION$') 9 | end_solution = re.compile(r' *// END SOLUTION$') 10 | prompt = re.compile(r'// PROMPT ') 11 | home_url_placeholder = "http://home.url" 12 | 13 | if len(sys.argv) <= 1: 14 | sys.stderr.write("Missing home-url argument\n"); 15 | sys.exit(1) 16 | 17 | home_url = sys.argv[1] 18 | 19 | def replace(mo): 20 | path = mo.group(1) 21 | file_lines = file("src/%s.rs" % path).read().split('\n') 22 | state = 'keep' 23 | output_lines = [] 24 | for line in file_lines: 25 | if state == 'skip': 26 | if end_solution.match(line): 27 | state = 'keep' 28 | elif start_solution.match(line): 29 | raise Exception("Unmatched START SOLUTION in %s" % path) 30 | elif start_solution.match(line): 31 | state = 'skip' 32 | elif end_solution.match(line): 33 | raise Exception("Unmatched END SOLUTION in %s" % path) 34 | else: 35 | line = prompt.sub("", line) 36 | line = line.replace(home_url_placeholder, home_url) 37 | output_lines.append(line) 38 | file_contents = '\n'.join(output_lines) 39 | query = urlencode({"version": "nightly", "code": file_contents}) 40 | return 'href="https://play.rust-lang.org/?%s"' % query 41 | 42 | for line in sys.stdin: 43 | line = href.sub(replace, line) 44 | line = line.replace(home_url_placeholder, home_url) 45 | sys.stdout.write(line) 46 | 47 | --------------------------------------------------------------------------------