├── .gitignore ├── Cargo.toml ├── Makefile ├── index-template.md ├── lesson-plan.md ├── src ├── actor.rs ├── borrowing.rs ├── channels.rs ├── channels_sol.rs ├── example01.rs ├── example02.rs ├── example03.rs ├── example04.rs ├── example05.rs ├── hello_world.rs ├── main.rs ├── mutex.rs ├── parallel_search.rs ├── parallel_search_sol.rs ├── sequential_search.rs └── shared_memory.rs └── sync-index.py /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | cargo.lock 3 | index.html 4 | *~ 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tutorial-concurrency" 3 | version = "0.1.0" 4 | authors = ["Niko Matsakis "] 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | index.html: index-template.md sync-index.py $(wildcard src/*rs) 2 | markdown index-template.md | python sync-index.py > index.html 3 | -------------------------------------------------------------------------------- /index-template.md: -------------------------------------------------------------------------------- 1 | ## Rust concurrency tutorial 2 | 3 | - [Hello World](src/hello_world.rs): 4 | - Goal: make it greet you by name 5 | - Time: 3 minutes 6 | - [Borrowing](src/borrowing.rs): 7 | - Goal: convert this code to use borrowing instead so that it compiles 8 | - Time: 3 minutes 9 | - [Sequential search](src/sequential_search.rs): 10 | - Goal: implement `find_best_store` 11 | - Time: 10 minutes 12 | - [Parallel search](src/parallel_search.rs): 13 | - Goal: make this actually run in parallel 14 | - Hint: you will have to stop cloning the store name 15 | - Time: 10 minutes 16 | - [Channels](src/channels.rs): 17 | - Goal: modify parallel `find_best_store` to use channels 18 | - Extra bonus: modify to make each store an actor and use RPC 19 | - Time: 10 minutes 20 | - [Shared memory](src/shared_memory.rs): 21 | - Goal: Adapt your previous code to use `Arc` 22 | - Extra bonus: How can you minimize cloning the underlying shopping list? 23 | - Time: 10 minutes 24 | - [Introduce mutex](src/mutex.rs): 25 | - Goal: Adapt your previous code to introduce a mutex and use it to find the best price 26 | - Time: 10 minutes 27 | 28 | Thank you for coming to the tutorial! Before you go, please let me 29 | know what you thought at 30 | [this Google form](http://goo.gl/forms/CN4trE3rXe). 31 | -------------------------------------------------------------------------------- /lesson-plan.md: -------------------------------------------------------------------------------- 1 | - Intro slide 2 | - Hello, World Example 3 | - Explain: 4 | - `fn` declaration 5 | - `println` is a macro 6 | - Format strings in `println!` 7 | - `println("Hello, {}!", world);` 8 | - also show `{:?}` 9 | - Move "world" into a local variable so we can change it 10 | - `let name = "Rustacean"; println("Hello, {}!", name);` 11 | - Abstract into a helper fn 12 | - `fn greet(name: String) { println("Hello, {}!", name); }` 13 | - What goes wrong? 14 | - Explain `format!`, show how you can use same helpers 15 | - Explain `push_str` and mutable local variables 16 | - `let mut name = format!("fellow "); name.push_str("Rustacean");` 17 | - Call helper fn twice 18 | - What goes wrong now? 19 | - Ownership slides 20 | - Borrowing slides 21 | - Borrowing example 22 | - Show that `&` and `&mut` cannot overlap 23 | - Time-limited 24 | - For loops 25 | - show that `s` has type `String` 26 | - Options and enums 27 | - explain about `unwrap` and how it is risky 28 | - show `let x = None` and unwrap 29 | - modify to some other example, like Shape / Square / Circle 30 | - Sequential search 31 | - 10 minutes, wonder about 32 | - Parallel search 33 | - note the `use` statement 34 | - let's look at these two curious clones: 35 | - we'll start with `shopping_list` 36 | - type of `shopping_list` is in fact a reference 37 | - threads can't close over a reference 38 | - try to remove the clone, see what happens 39 | - what about `name`? 40 | - `name` is needed because `store` is moved into 41 | - how do we parallelize? 42 | - we have to start ALL the threads before we join ANY of them 43 | - Shared memory exercise 44 | - Afterwards: 45 | - show how you can change so that we request an 46 | `Vec` instead of `&Vec`, note that in general if 47 | you will consume data, it's best to take it by value, and let 48 | the owner clone if they must 49 | - note that this applies equally well when using channels, 50 | and in fact a common pattern is to send an `Arc` over a channel 51 | -------------------------------------------------------------------------------- /src/actor.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::collections::HashMap; 4 | use std::f32::INFINITY; 5 | use std::sync::mpsc::{channel, Sender, Receiver}; 6 | use std::thread; 7 | 8 | struct Store { 9 | name: String, 10 | prices: HashMap, 11 | } 12 | 13 | impl Store { 14 | fn new(name: String) -> Store { 15 | Store { 16 | name: name, 17 | prices: HashMap::new(), 18 | } 19 | } 20 | 21 | fn add_item(&mut self, name: String, price: f32) { 22 | self.prices.insert(name, price); 23 | } 24 | 25 | fn price(&self, item_name: &str) -> f32 { 26 | self.prices[item_name] 27 | } 28 | } 29 | 30 | fn build_stores() -> Vec { 31 | let mut stores = vec![]; 32 | 33 | let mut store = Store::new(format!("R-mart")); 34 | store.add_item(format!("chocolate"), 5.0); 35 | store.add_item(format!("doll"), 22.0); 36 | store.add_item(format!("bike"), 150.0); 37 | stores.push(store); 38 | 39 | let mut store = Store::new(format!("Bullseye")); 40 | store.add_item(format!("chocolate"), 2.0); 41 | store.add_item(format!("doll"), 23.0); 42 | store.add_item(format!("bike"), 145.0); 43 | stores.push(store); 44 | 45 | let mut store = Store::new(format!("Woolmart")); 46 | store.add_item(format!("chocolate"), 2.0); 47 | store.add_item(format!("doll"), 23.0); 48 | store.add_item(format!("bike"), 146.0); 49 | stores.push(store); 50 | 51 | stores 52 | } 53 | 54 | enum Message { 55 | PriceOf(String, Sender), 56 | StoreName(Sender), 57 | } 58 | 59 | fn store_thread(store: Store, port: Receiver) { 60 | for msg in port { 61 | match msg { 62 | Message::PriceOf(item_name, respond_to) => 63 | respond_to.send(store.price(&item_name)).unwrap(), 64 | Message::StoreName(respond_to) => 65 | respond_to.send(store.name.clone()).unwrap(), 66 | } 67 | } 68 | } 69 | 70 | fn find_best_store(stores: Vec, shopping_list: &Vec) -> String { 71 | assert!(stores.len() > 0); 72 | 73 | let store_txs: Vec<_> = 74 | stores.into_iter() 75 | .map(|store| { 76 | let (tx, rx) = channel(); 77 | thread::spawn(move || store_thread(store, rx)); 78 | tx 79 | }) 80 | .collect(); 81 | 82 | let mut best = None; 83 | let mut best_price = INFINITY; 84 | for store_tx in store_txs { 85 | let mut sum = 0.0; 86 | for item in shopping_list { 87 | let (price_tx, price_rx) = channel(); 88 | store_tx.send(Message::PriceOf(item.clone(), price_tx)).unwrap(); 89 | let price = price_rx.recv().unwrap(); 90 | sum += price; 91 | } 92 | if sum < best_price { 93 | let (name_tx, name_rx) = channel(); 94 | store_tx.send(Message::StoreName(name_tx)).unwrap(); 95 | let name = name_rx.recv().unwrap(); 96 | best = Some(name); 97 | best_price = sum; 98 | } 99 | } 100 | 101 | best.unwrap() // there will always be at least one store 102 | } 103 | 104 | fn compute_sum(store: &Store, shopping_list: &Vec) -> f32 { 105 | shopping_list.iter() 106 | .map(|item_name| store.price(item_name)) 107 | .fold(0.0, |v, u| v + u) 108 | } 109 | 110 | pub fn main() { 111 | let shopping_list = vec![format!("chocolate"), 112 | format!("doll"), 113 | format!("bike")]; 114 | let stores = build_stores(); 115 | let best_store = find_best_store(stores, &shopping_list); 116 | println!("Best store: {}", best_store); 117 | } 118 | 119 | -------------------------------------------------------------------------------- /src/borrowing.rs: -------------------------------------------------------------------------------- 1 | pub fn main() { 2 | let mut name = format!("fellow "); 3 | name.push_str("Rustacean"); 4 | helper(name); 5 | helper(name); 6 | } 7 | 8 | fn helper(name: String) { 9 | println!("Hello, {}!", name); 10 | } 11 | -------------------------------------------------------------------------------- /src/channels.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::collections::HashMap; 4 | use std::f32::INFINITY; 5 | use std::sync::mpsc::channel; 6 | use std::thread; 7 | 8 | struct Store { 9 | name: String, 10 | prices: HashMap, 11 | } 12 | 13 | impl Store { 14 | fn new(name: String) -> Store { 15 | Store { 16 | name: name, 17 | prices: HashMap::new(), 18 | } 19 | } 20 | 21 | fn add_item(&mut self, name: String, price: f32) { 22 | self.prices.insert(name, price); 23 | } 24 | 25 | fn price(&self, item_name: &str) -> f32 { 26 | self.prices[item_name] 27 | } 28 | } 29 | 30 | fn build_stores() -> Vec { 31 | let mut stores = vec![]; 32 | 33 | let mut store = Store::new(format!("R-mart")); 34 | store.add_item(format!("chocolate"), 5.0); 35 | store.add_item(format!("doll"), 22.0); 36 | store.add_item(format!("bike"), 150.0); 37 | stores.push(store); 38 | 39 | let mut store = Store::new(format!("Bullseye")); 40 | store.add_item(format!("chocolate"), 2.0); 41 | store.add_item(format!("doll"), 23.0); 42 | store.add_item(format!("bike"), 145.0); 43 | stores.push(store); 44 | 45 | let mut store = Store::new(format!("Woolmart")); 46 | store.add_item(format!("chocolate"), 2.0); 47 | store.add_item(format!("doll"), 23.0); 48 | store.add_item(format!("bike"), 146.0); 49 | stores.push(store); 50 | 51 | stores 52 | } 53 | 54 | fn find_best_store(stores: Vec, shopping_list: &Vec) -> String { 55 | assert!(stores.len() > 0); 56 | 57 | // TODO: 58 | // - create channel for receiving message 59 | // - start threads for each store, giving each a `tx` to send messages to 60 | // - have each thread send its name and sum 61 | // - find and return best name 62 | 63 | for store in stores { 64 | let shopping_list = shopping_list.clone(); 65 | thread::spawn(move || { 66 | let sum = compute_sum(&store, &shopping_list); 67 | unimplemented!(); // send `sum` and `store.name` over channel 68 | }); 69 | } 70 | 71 | let mut best = None; 72 | let mut best_price = INFINITY; 73 | loop { // <-- change to some suitable loop! 74 | let (sum, name) = unimplemented!(); // receive `sum` and `name` from channel 75 | if sum < best_price { 76 | best = Some(name); 77 | best_price = sum; 78 | } 79 | } 80 | 81 | best.unwrap() // there will always be at least one store 82 | } 83 | 84 | fn compute_sum(store: &Store, shopping_list: &Vec) -> f32 { 85 | shopping_list.iter() 86 | .map(|item_name| store.price(item_name)) 87 | .fold(0.0, |v, u| v + u) 88 | } 89 | 90 | pub fn main() { 91 | let shopping_list = vec![format!("chocolate"), 92 | format!("doll"), 93 | format!("bike")]; 94 | let stores = build_stores(); 95 | let best_store = find_best_store(stores, &shopping_list); 96 | println!("Best store: {}", best_store); 97 | } 98 | 99 | -------------------------------------------------------------------------------- /src/channels_sol.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::collections::HashMap; 4 | use std::f32::INFINITY; 5 | use std::mem; 6 | use std::sync::mpsc::channel; 7 | use std::thread; 8 | 9 | struct Store { 10 | name: String, 11 | prices: HashMap, 12 | } 13 | 14 | impl Store { 15 | fn new(name: String) -> Store { 16 | Store { 17 | name: name, 18 | prices: HashMap::new(), 19 | } 20 | } 21 | 22 | fn add_item(&mut self, name: String, price: f32) { 23 | self.prices.insert(name, price); 24 | } 25 | 26 | fn price(&self, item_name: &str) -> f32 { 27 | self.prices[item_name] 28 | } 29 | } 30 | 31 | fn build_stores() -> Vec { 32 | let mut stores = vec![]; 33 | 34 | let mut store = Store::new(format!("R-mart")); 35 | store.add_item(format!("chocolate"), 5.0); 36 | store.add_item(format!("doll"), 22.0); 37 | store.add_item(format!("bike"), 150.0); 38 | stores.push(store); 39 | 40 | let mut store = Store::new(format!("Bullseye")); 41 | store.add_item(format!("chocolate"), 2.0); 42 | store.add_item(format!("doll"), 23.0); 43 | store.add_item(format!("bike"), 145.0); 44 | stores.push(store); 45 | 46 | let mut store = Store::new(format!("Woolmart")); 47 | store.add_item(format!("chocolate"), 2.0); 48 | store.add_item(format!("doll"), 23.0); 49 | store.add_item(format!("bike"), 146.0); 50 | stores.push(store); 51 | 52 | stores 53 | } 54 | 55 | fn find_best_store(stores: Vec, shopping_list: &Vec) -> String { 56 | assert!(stores.len() > 0); 57 | 58 | let (tx, rx) = channel(); 59 | for store in stores { 60 | let shopping_list = shopping_list.clone(); 61 | let tx = tx.clone(); 62 | thread::spawn(move || { 63 | let sum = compute_sum(&store, &shopping_list); 64 | tx.send((sum, store.name)).unwrap(); 65 | }); 66 | } 67 | mem::drop(tx); 68 | 69 | let mut best = None; 70 | let mut best_price = INFINITY; 71 | for (sum, name) in rx { 72 | if sum < best_price { 73 | best = Some(name); 74 | best_price = sum; 75 | } 76 | } 77 | 78 | best.unwrap() // there will always be at least one store 79 | } 80 | 81 | fn compute_sum(store: &Store, shopping_list: &Vec) -> f32 { 82 | shopping_list.iter() 83 | .map(|item_name| store.price(item_name)) 84 | .fold(0.0, |v, u| v + u) 85 | } 86 | 87 | pub fn main() { 88 | let shopping_list = vec![format!("chocolate"), 89 | format!("doll"), 90 | format!("bike")]; 91 | let stores = build_stores(); 92 | let best_store = find_best_store(stores, &shopping_list); 93 | println!("Best store: {}", best_store); 94 | } 95 | 96 | -------------------------------------------------------------------------------- /src/example01.rs: -------------------------------------------------------------------------------- 1 | pub fn main() { 2 | let name = format!("fellow Rustacean"); 3 | helper(name); 4 | } 5 | 6 | fn helper(name: String) { 7 | println!("Hello, {}!", name); 8 | } 9 | 10 | // Discuss: 11 | // - &'static str vs String 12 | // - lead to 13 | -------------------------------------------------------------------------------- /src/example02.rs: -------------------------------------------------------------------------------- 1 | pub fn main() { 2 | let mut name = format!("fellow "); 3 | name.push_str("Rustacean"); 4 | helper(name); 5 | } 6 | 7 | fn helper(name: String) { 8 | println!("Hello, {}!", name); 9 | } 10 | -------------------------------------------------------------------------------- /src/example03.rs: -------------------------------------------------------------------------------- 1 | pub fn main() { 2 | let mut name = format!("fellow "); 3 | name.push_str("Rustacean"); 4 | helper(&name); 5 | helper(&name); 6 | } 7 | 8 | fn helper(name: &String) { 9 | println!("Hello, {}!", name); 10 | } 11 | -------------------------------------------------------------------------------- /src/example04.rs: -------------------------------------------------------------------------------- 1 | pub fn main() { 2 | let mut name = format!("fellow "); 3 | adjust(&mut name); 4 | helper(&name); 5 | helper(&name); 6 | } 7 | 8 | fn adjust(name: &mut String) { 9 | name.push_str("Rustacean"); 10 | } 11 | 12 | fn helper(name: &String) { 13 | println!("Hello, {}!", name); 14 | } 15 | -------------------------------------------------------------------------------- /src/example05.rs: -------------------------------------------------------------------------------- 1 | pub fn main() { 2 | let mut name = Rc::new(format!("fellow ")); 3 | helper(name.clone()); 4 | helper(name.clone()); 5 | } 6 | 7 | fn helper(name: Rc) { 8 | println!("Hello, {}!", name); 9 | } 10 | -------------------------------------------------------------------------------- /src/hello_world.rs: -------------------------------------------------------------------------------- 1 | pub fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod hello_world; 2 | // mod borrowing; not expected to compile 3 | mod sequential_search; 4 | mod parallel_search; 5 | mod parallel_search_sol; 6 | mod channels; 7 | mod channels_sol; 8 | mod actor; 9 | mod shared_memory; 10 | mod mutex; 11 | 12 | fn main() { 13 | println!("----------------------------------------------------------------------"); 14 | hello_world::main(); 15 | 16 | println!("----------------------------------------------------------------------"); 17 | sequential_search::main(); 18 | 19 | println!("----------------------------------------------------------------------"); 20 | parallel_search::main(); 21 | parallel_search_sol::main(); 22 | 23 | println!("----------------------------------------------------------------------"); 24 | // channels::main(); // not expected to execute 25 | channels_sol::main(); 26 | 27 | println!("----------------------------------------------------------------------"); 28 | actor::main(); 29 | 30 | println!("----------------------------------------------------------------------"); 31 | shared_memory::main(); 32 | 33 | println!("----------------------------------------------------------------------"); 34 | mutex::main(); 35 | } 36 | -------------------------------------------------------------------------------- /src/mutex.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::f32::INFINITY; 3 | use std::sync::Arc; 4 | use std::sync::Mutex; 5 | use std::thread; 6 | 7 | struct Store { 8 | name: String, 9 | prices: HashMap, 10 | } 11 | 12 | impl Store { 13 | fn new(name: String) -> Store { 14 | Store { 15 | name: name, 16 | prices: HashMap::new(), 17 | } 18 | } 19 | 20 | fn add_item(&mut self, name: String, price: f32) { 21 | self.prices.insert(name, price); 22 | } 23 | 24 | fn price(&self, item_name: &str) -> f32 { 25 | self.prices[item_name] 26 | } 27 | } 28 | 29 | fn build_stores() -> Vec { 30 | let mut stores = vec![]; 31 | 32 | let mut store = Store::new(format!("R-mart")); 33 | store.add_item(format!("chocolate"), 5.0); 34 | store.add_item(format!("doll"), 22.0); 35 | store.add_item(format!("bike"), 150.0); 36 | stores.push(store); 37 | 38 | let mut store = Store::new(format!("Bullseye")); 39 | store.add_item(format!("chocolate"), 2.0); 40 | store.add_item(format!("doll"), 23.0); 41 | store.add_item(format!("bike"), 145.0); 42 | stores.push(store); 43 | 44 | let mut store = Store::new(format!("Woolmart")); 45 | store.add_item(format!("chocolate"), 2.0); 46 | store.add_item(format!("doll"), 23.0); 47 | store.add_item(format!("bike"), 146.0); 48 | stores.push(store); 49 | 50 | stores 51 | } 52 | 53 | fn find_best_store(stores: Vec, shopping_list: Vec) -> String { 54 | struct Best { 55 | name: Option, 56 | price: f32 57 | } 58 | 59 | let best = Arc::new(Mutex::new(Best { 60 | name: None, 61 | price: INFINITY 62 | })); 63 | let shopping_list = Arc::new(shopping_list); 64 | let threads: Vec<_> = 65 | stores.into_iter() 66 | .map(|store| { 67 | let best = best.clone(); 68 | let shopping_list = shopping_list.clone(); 69 | thread::spawn(move || { 70 | let sum = shopping_list.iter() 71 | .map(|item_name| store.price(item_name)) 72 | .fold(0.0, |v, u| v + u); 73 | let mut best = best.lock().unwrap(); // propagate panics 74 | if sum < best.price { 75 | best.name = Some(store.name); 76 | best.price = sum; 77 | } 78 | }) 79 | }) 80 | .collect(); 81 | 82 | for thread in threads { 83 | thread.join().unwrap(); // propoagate panics 84 | } 85 | 86 | let best = best.lock().unwrap(); 87 | best.name.clone().unwrap() 88 | } 89 | 90 | pub fn main() { 91 | let shopping_list = vec![format!("chocolate"), 92 | format!("doll"), 93 | format!("bike")]; 94 | 95 | let stores = build_stores(); 96 | let best_store = find_best_store(stores, shopping_list); 97 | println!("Best store: {}", best_store); 98 | } 99 | 100 | -------------------------------------------------------------------------------- /src/parallel_search.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::collections::HashMap; 4 | use std::f32::INFINITY; 5 | use std::thread; 6 | 7 | struct Store { 8 | name: String, 9 | prices: HashMap, 10 | } 11 | 12 | impl Store { 13 | fn new(name: String) -> Store { 14 | Store { 15 | name: name, 16 | prices: HashMap::new(), 17 | } 18 | } 19 | 20 | fn add_item(&mut self, name: String, price: f32) { 21 | self.prices.insert(name, price); 22 | } 23 | 24 | fn price(&self, item_name: &str) -> f32 { 25 | self.prices[item_name] 26 | } 27 | } 28 | 29 | fn build_stores() -> Vec { 30 | let mut stores = vec![]; 31 | 32 | let mut store = Store::new(format!("R-mart")); 33 | store.add_item(format!("chocolate"), 5.0); 34 | store.add_item(format!("doll"), 22.0); 35 | store.add_item(format!("bike"), 150.0); 36 | stores.push(store); 37 | 38 | let mut store = Store::new(format!("Bullseye")); 39 | store.add_item(format!("chocolate"), 2.0); 40 | store.add_item(format!("doll"), 23.0); 41 | store.add_item(format!("bike"), 145.0); 42 | stores.push(store); 43 | 44 | let mut store = Store::new(format!("Woolmart")); 45 | store.add_item(format!("chocolate"), 2.0); 46 | store.add_item(format!("doll"), 23.0); 47 | store.add_item(format!("bike"), 146.0); 48 | stores.push(store); 49 | 50 | stores 51 | } 52 | 53 | fn find_best_store(stores: Vec, shopping_list: &Vec) -> String { 54 | assert!(stores.len() > 0); 55 | let mut best = None; 56 | let mut best_price = INFINITY; 57 | for store in stores { 58 | let shopping_list = shopping_list.clone(); 59 | let name = store.name.clone(); 60 | let handle = thread::spawn(move || { 61 | compute_sum(&store, &shopping_list) 62 | }); 63 | let sum = handle.join().unwrap(); 64 | if sum < best_price { 65 | best = Some(name); 66 | best_price = sum; 67 | } 68 | } 69 | best.unwrap() // there will always be at least one store 70 | } 71 | 72 | fn compute_sum(store: &Store, shopping_list: &Vec) -> f32 { 73 | shopping_list.iter() 74 | .map(|item_name| store.price(item_name)) 75 | .fold(0.0, |v, u| v + u) 76 | } 77 | 78 | pub fn main() { 79 | let shopping_list = vec![format!("chocolate"), 80 | format!("doll"), 81 | format!("bike")]; 82 | let stores = build_stores(); 83 | let best_store = find_best_store(stores, &shopping_list); 84 | println!("Best store: {}", best_store); 85 | } 86 | 87 | -------------------------------------------------------------------------------- /src/parallel_search_sol.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::f32::INFINITY; 3 | use std::thread; 4 | 5 | struct Store { 6 | name: String, 7 | prices: HashMap, 8 | } 9 | 10 | impl Store { 11 | fn new(name: String) -> Store { 12 | Store { 13 | name: name, 14 | prices: HashMap::new(), 15 | } 16 | } 17 | 18 | fn add_item(&mut self, name: String, price: f32) { 19 | self.prices.insert(name, price); 20 | } 21 | 22 | fn price(&self, item_name: &str) -> f32 { 23 | self.prices[item_name] 24 | } 25 | } 26 | 27 | fn build_stores() -> Vec { 28 | let mut stores = vec![]; 29 | 30 | let mut store = Store::new(format!("R-mart")); 31 | store.add_item(format!("chocolate"), 5.0); 32 | store.add_item(format!("doll"), 22.0); 33 | store.add_item(format!("bike"), 150.0); 34 | stores.push(store); 35 | 36 | let mut store = Store::new(format!("Bullseye")); 37 | store.add_item(format!("chocolate"), 2.0); 38 | store.add_item(format!("doll"), 23.0); 39 | store.add_item(format!("bike"), 145.0); 40 | stores.push(store); 41 | 42 | let mut store = Store::new(format!("Woolmart")); 43 | store.add_item(format!("chocolate"), 2.0); 44 | store.add_item(format!("doll"), 23.0); 45 | store.add_item(format!("bike"), 146.0); 46 | stores.push(store); 47 | 48 | stores 49 | } 50 | 51 | fn find_best_store(stores: Vec, shopping_list: &Vec) -> String { 52 | let threads: Vec<_> = 53 | stores.into_iter() 54 | .map(|store| { 55 | let shopping_list = shopping_list.clone(); 56 | thread::spawn(move || { 57 | let sum = compute_sum(&store, &shopping_list); 58 | (store.name, sum) 59 | }) 60 | }) 61 | .collect(); 62 | 63 | let mut best = None; 64 | let mut best_price = INFINITY; 65 | for thread in threads { 66 | let (name, sum) = thread.join().unwrap(); // propoagate panics 67 | if sum < best_price { 68 | best = Some(name); 69 | best_price = sum; 70 | } 71 | } 72 | best.unwrap() 73 | } 74 | 75 | fn compute_sum(store: &Store, shopping_list: &Vec) -> f32 { 76 | shopping_list.iter() 77 | .map(|item_name| store.price(item_name)) 78 | .fold(0.0, |v, u| v + u) 79 | } 80 | 81 | pub fn main() { 82 | let shopping_list = vec![format!("chocolate"), 83 | format!("doll"), 84 | format!("bike")]; 85 | 86 | let stores = build_stores(); 87 | let best_store = find_best_store(stores, &shopping_list); 88 | println!("Best store: {}", best_store); 89 | } 90 | 91 | -------------------------------------------------------------------------------- /src/sequential_search.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::f32::INFINITY; 3 | 4 | struct Store { 5 | name: String, 6 | prices: HashMap, 7 | } 8 | 9 | impl Store { 10 | fn new(name: String) -> Store { 11 | Store { 12 | name: name, 13 | prices: HashMap::new(), 14 | } 15 | } 16 | 17 | fn add_item(&mut self, name: String, price: f32) { 18 | self.prices.insert(name, price); 19 | } 20 | 21 | fn price(&self, item_name: &str) -> f32 { 22 | self.prices[item_name] 23 | } 24 | } 25 | 26 | fn build_stores() -> Vec { 27 | let mut stores = vec![]; 28 | 29 | let mut store = Store::new(format!("R-mart")); 30 | store.add_item(format!("chocolate"), 5.0); 31 | store.add_item(format!("doll"), 22.0); 32 | store.add_item(format!("bike"), 150.0); 33 | stores.push(store); 34 | 35 | let mut store = Store::new(format!("Bullseye")); 36 | store.add_item(format!("chocolate"), 2.0); 37 | store.add_item(format!("doll"), 23.0); 38 | store.add_item(format!("bike"), 145.0); 39 | stores.push(store); 40 | 41 | let mut store = Store::new(format!("Woolmart")); 42 | store.add_item(format!("chocolate"), 2.0); 43 | store.add_item(format!("doll"), 23.0); 44 | store.add_item(format!("bike"), 146.0); 45 | stores.push(store); 46 | 47 | stores 48 | } 49 | 50 | fn find_best_store(stores: Vec, shopping_list: &Vec) -> String { 51 | // EXERCISE 0 52 | // 53 | // Find the store with the best price and return its name. 54 | assert!(stores.len() > 0); 55 | let mut best = None; 56 | let mut best_price = INFINITY; 57 | for store in stores { 58 | let sum = compute_sum(&store, shopping_list); 59 | if sum < best_price { 60 | best = Some(store.name); 61 | best_price = sum; 62 | } 63 | } 64 | best.unwrap() // there will always be at least one store 65 | } 66 | 67 | fn compute_sum(store: &Store, shopping_list: &Vec) -> f32 { 68 | shopping_list.iter() 69 | .map(|item_name| store.price(item_name)) 70 | .fold(0.0, |v, u| v + u) 71 | } 72 | 73 | pub fn main() { 74 | let shopping_list = vec![format!("chocolate"), 75 | format!("doll"), 76 | format!("bike")]; 77 | let stores = build_stores(); 78 | let best_store = find_best_store(stores, &shopping_list); 79 | println!("Best store: {}", best_store); 80 | } 81 | 82 | -------------------------------------------------------------------------------- /src/shared_memory.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::f32::INFINITY; 3 | use std::sync::Arc; 4 | use std::thread; 5 | 6 | struct Store { 7 | name: String, 8 | prices: HashMap, 9 | } 10 | 11 | impl Store { 12 | fn new(name: String) -> Store { 13 | Store { 14 | name: name, 15 | prices: HashMap::new(), 16 | } 17 | } 18 | 19 | fn add_item(&mut self, name: String, price: f32) { 20 | self.prices.insert(name, price); 21 | } 22 | 23 | fn price(&self, item_name: &str) -> f32 { 24 | self.prices[item_name] 25 | } 26 | } 27 | 28 | fn build_stores() -> Vec { 29 | let mut stores = vec![]; 30 | 31 | let mut store = Store::new(format!("R-mart")); 32 | store.add_item(format!("chocolate"), 5.0); 33 | store.add_item(format!("doll"), 22.0); 34 | store.add_item(format!("bike"), 150.0); 35 | stores.push(store); 36 | 37 | let mut store = Store::new(format!("Bullseye")); 38 | store.add_item(format!("chocolate"), 2.0); 39 | store.add_item(format!("doll"), 23.0); 40 | store.add_item(format!("bike"), 145.0); 41 | stores.push(store); 42 | 43 | let mut store = Store::new(format!("Woolmart")); 44 | store.add_item(format!("chocolate"), 2.0); 45 | store.add_item(format!("doll"), 23.0); 46 | store.add_item(format!("bike"), 146.0); 47 | stores.push(store); 48 | 49 | stores 50 | } 51 | 52 | fn find_best_store(stores: Vec, shopping_list: Vec) -> String { 53 | let shopping_list = Arc::new(shopping_list); 54 | let threads: Vec<_> = 55 | stores.into_iter() 56 | .map(|store| { 57 | let shopping_list = shopping_list.clone(); 58 | thread::spawn(move || { 59 | let sum = shopping_list.iter() 60 | .map(|item_name| store.price(item_name)) 61 | .fold(0.0, |v, u| v + u); 62 | (store.name, sum) 63 | }) 64 | }) 65 | .collect(); 66 | 67 | let mut best = None; 68 | let mut best_price = INFINITY; 69 | for thread in threads { 70 | let (name, sum) = thread.join().unwrap(); // propoagate panics 71 | if sum < best_price { 72 | best = Some(name); 73 | best_price = sum; 74 | } 75 | } 76 | best.unwrap() 77 | } 78 | 79 | pub fn main() { 80 | let shopping_list = vec![format!("chocolate"), 81 | format!("doll"), 82 | format!("bike")]; 83 | 84 | let stores = build_stores(); 85 | let best_store = find_best_store(stores, shopping_list); 86 | println!("Best store: {}", best_store); 87 | } 88 | 89 | -------------------------------------------------------------------------------- /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 | 9 | def replace(mo): 10 | path = mo.group(1) 11 | file_contents = file("src/%s.rs" % path).read() 12 | query = urlencode({"version": "stable", "code": file_contents}) 13 | return 'href="https://play.rust-lang.org/?%s"' % query 14 | 15 | 16 | for line in sys.stdin: 17 | line2 = href.sub(replace, line) 18 | sys.stdout.write(line2) 19 | 20 | --------------------------------------------------------------------------------