├── .gitignore ├── Chapter01 ├── Cargo.toml ├── README.md ├── intro_binding.rs ├── intro_datatypes.rs ├── intro_expressions.rs ├── intro_functions.rs ├── intro_generics.rs ├── intro_iterators.rs ├── intro_metaprogramming.rs ├── intro_mixoopfp.rs ├── intro_patterns.rs └── metaderive │ ├── Cargo.toml │ └── src │ └── lib.rs ├── Chapter02 ├── Cargo.toml ├── src │ ├── lib.rs │ └── main.rs ├── test1.txt └── tests │ └── integration_tests.rs ├── Chapter03 ├── .gitignore ├── Cargo.toml ├── src │ ├── analyze.rs │ ├── lib.rs │ ├── main.rs │ ├── motor.rs │ └── physics.rs ├── test1.txt └── tests │ └── integration_tests.rs ├── Chapter04 ├── Cargo.toml ├── gadt.rs ├── generics.rs ├── lifetimes.rs ├── parameters.rs ├── polymorphism.rs ├── serialize.rs └── src │ ├── analyze.rs │ ├── lib.rs │ ├── main.rs │ ├── motor.rs │ └── physics.rs ├── Chapter05 ├── .gitignore ├── Cargo.toml ├── README.md ├── build.rs ├── src │ ├── analyze_trip.rs │ ├── buildings.rs │ ├── data_recorders.rs │ ├── elevator1.c │ ├── elevator2.c │ ├── elevator3.c │ ├── elevator_drivers.rs │ ├── lib.rs │ ├── motion_controllers.rs │ ├── motor1.c │ ├── motor2.c │ ├── motor3.c │ ├── motor_controllers.rs │ ├── operate_elevator.rs │ ├── physics.rs │ ├── simulate_trip.rs │ └── trip_planning.rs └── test1.txt ├── Chapter06 ├── Cargo.toml ├── build.rs ├── fixed │ ├── .gitignore │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ ├── admin.rs │ │ ├── elevator_magic.c │ │ ├── lib.rs │ │ ├── magic.rs │ │ └── tests │ │ ├── admin.rs │ │ ├── magic.rs │ │ └── mod.rs ├── mutability.rs ├── pure_functions.rs └── src │ ├── admin.rs │ ├── elevator_magic.c │ ├── lib.rs │ ├── magic.rs │ └── tests │ ├── admin.rs │ ├── magic.rs │ └── mod.rs ├── Chapter07 ├── Cargo.toml ├── combinator_pattern.rs ├── functor_pattern.rs ├── lazy_pattern.rs └── monad_pattern.rs ├── Chapter08 ├── .gitignore ├── Cargo.toml ├── fork1.rs ├── fork2.rs ├── fork3.rs ├── pattern1.rs ├── pattern2.rs ├── pattern3.rs ├── process_a.rs ├── process_b.rs ├── share1.rs ├── share2.rs ├── share3.rs ├── share4.rs ├── share5.rs ├── share6.rs ├── share7.rs ├── thread1.rs ├── thread2.rs ├── thread3.rs ├── thread4.rs └── thread5.rs ├── Chapter09 ├── .gitignore ├── Cargo.toml ├── README.md ├── debugging_assert.rs ├── debugging_buggy_worker.rs ├── debugging_heartbeat.rs ├── debugging_result.rs ├── metaprogramming_ebnf.rs ├── metaprogramming_grammar.rs ├── metaprogramming_procmacro.rs ├── metaprogramming_procmacro2.rs ├── performance_constant.rs ├── performance_exponential.rs ├── performance_logarithmic.rs ├── performance_omission1.rs ├── performance_omission2.rs ├── performance_polynomial1.rs ├── performance_polynomial2.rs ├── performance_polynomial3.rs ├── performance_polynomial4.rs ├── performance_profiling1.rs ├── performance_profiling2.rs ├── performance_profiling3.rs ├── performance_profiling4.rs ├── performance_reference.rs ├── performance_release_mode.rs ├── procmacro │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── procmacro2 │ ├── .gitignore │ ├── Cargo.toml │ └── src │ └── lib.rs ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | Chapter*/Cargo.lock 2 | Chapter*/target 3 | -------------------------------------------------------------------------------- /Chapter01/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "Chapter1" 3 | version = "1.0.0" 4 | 5 | [dependencies] 6 | lazy_static = "1" 7 | cached = "0.5" 8 | metaderive = { path = "metaderive" } 9 | 10 | [[bin]] 11 | name = "intro_binding" 12 | path = "intro_binding.rs" 13 | 14 | [[bin]] 15 | name = "intro_datatypes" 16 | path = "intro_datatypes.rs" 17 | 18 | [[bin]] 19 | name = "intro_expressions" 20 | path = "intro_expressions.rs" 21 | 22 | [[bin]] 23 | name = "intro_functions" 24 | path = "intro_functions.rs" 25 | 26 | [[bin]] 27 | name = "intro_generics" 28 | path = "intro_generics.rs" 29 | 30 | [[bin]] 31 | name = "intro_iterators" 32 | path = "intro_iterators.rs" 33 | 34 | [[bin]] 35 | name = "intro_metaprogramming" 36 | path = "intro_metaprogramming.rs" 37 | 38 | [[bin]] 39 | name = "intro_mixoopfp" 40 | path = "intro_mixoopfp.rs" 41 | 42 | [[bin]] 43 | name = "intro_patterns" 44 | path = "intro_patterns.rs" 45 | -------------------------------------------------------------------------------- /Chapter01/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 – Hands On Functional Programming in RUST 2 | To build all examples from each chapter section, call "cargo build" from the command line. Executables will be created in the target/debug folder. 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Chapter01/intro_binding.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::sync::{Mutex, Arc}; 3 | use std::sync::mpsc::channel; 4 | 5 | fn scoped() { 6 | vec![1, 2, 3]; 7 | } 8 | 9 | fn scoped2() -> Vec { 10 | vec![1, 2, 3] 11 | } 12 | 13 | fn scoped3() { 14 | let v1 = vec![1, 2, 3]; 15 | let v2 = v1; 16 | //it is now illegal to reference v1, 17 | //because ownership has been transferred to v2 18 | } 19 | 20 | fn scoped4() { 21 | vec![1, 2, 3].clone(); 22 | "".to_string().clone(); 23 | } 24 | 25 | fn scoped5() { 26 | fn foo(v1: &Vec) 27 | { 28 | for v in v1 29 | { 30 | println!("{}", v); 31 | } 32 | } 33 | 34 | let v1 = vec![1, 2, 3]; 35 | foo(&v1); 36 | //v1 is still valid, ownership has been returned 37 | v1; 38 | } 39 | 40 | fn thread1() { 41 | let v = vec![1, 2, 3]; 42 | let handle = thread::spawn(move || { 43 | println!("Here's a vector: {:?}", v); 44 | }); 45 | handle.join().ok(); 46 | } 47 | 48 | fn thread2() 49 | { 50 | let counter = Arc::new(Mutex::new(0)); 51 | let mut handles = vec![]; 52 | for _ in 0..10 { 53 | let counter = Arc::clone(&counter); 54 | let handle = thread::spawn(move || { 55 | let mut num = counter.lock().unwrap(); 56 | *num += 1; 57 | }); 58 | handles.push(handle); 59 | } 60 | for handle in handles { 61 | handle.join().unwrap(); 62 | } 63 | println!("Result: {}", *counter.lock().unwrap()); 64 | } 65 | 66 | fn thread3() { 67 | let (sender, receiver) = channel(); 68 | let handle = thread::spawn(move || { 69 | //do work 70 | let v = vec![1, 2, 3]; 71 | sender.send(v).unwrap(); 72 | }); 73 | handle.join().ok(); 74 | receiver.recv().unwrap(); 75 | } 76 | 77 | fn main() { 78 | } 79 | -------------------------------------------------------------------------------- /Chapter01/intro_datatypes.rs: -------------------------------------------------------------------------------- 1 | //It can be helpful to create shorthand names for complex types 2 | //alias 3 | type Name = String; 4 | 5 | //newtype 6 | struct NewName(String); 7 | 8 | 9 | //Structs can be repetitive if you just want a way to store multiple values together 10 | struct Data1 11 | { 12 | a: i32, 13 | b: f64, 14 | c: String 15 | } 16 | struct Data2 17 | { 18 | a: u32, 19 | b: String, 20 | c: f64 21 | } 22 | 23 | //Tuples help eliminate redundant struct definitions 24 | //no prior type definitions are needed here and the aliases are redundant 25 | type Tuple1 = (i32, f64, String); 26 | type Tuple2 = (u32, String, f64); 27 | 28 | //these types are not equivalent to tuples 29 | struct New1(i32, f64, String); 30 | struct New2(u32, String, f64); 31 | 32 | 33 | //Standard operators can be implemented with traits 34 | //anyone coming from an ML family language may appreciate 35 | use std::ops::Mul; 36 | 37 | struct Point 38 | { 39 | x: i32, 40 | y: i32 41 | } 42 | 43 | impl Mul for Point 44 | { 45 | type Output = Point; 46 | fn mul(self, other: Point) -> Point 47 | { 48 | Point 49 | { 50 | x: self.x * other.x, 51 | y: self.y * other.y 52 | } 53 | } 54 | } 55 | 56 | //Standard library collections etc. are generic 57 | use std::collections::HashMap; 58 | type CustomHashMap = HashMap; 59 | 60 | //Tagged Unions can be used to create typesafe definitions of structures that can't be safely described in pure OOP 61 | enum BTree 62 | { 63 | Branch { val:T, left:Box>, right:Box> }, 64 | Leaf { val:T } 65 | } 66 | 67 | //Commonly, Tagged Unions are used for complex data structures with many possible union options 68 | enum Term 69 | { 70 | TermVal { value: String }, 71 | TermVar { symbol: String }, 72 | TermApp { f: Box, x: Box }, 73 | TermAbs { arg: String, body: Box } 74 | } 75 | 76 | //Traits are a bit like Object Classes 77 | trait Data1Trait 78 | { 79 | //Traits can define constructors 80 | fn new(a: i32, b: f64, c: String) -> Self; 81 | 82 | //Traits can have methods, which reference "self" 83 | fn get_a(&self) -> i32; 84 | fn get_b(&self) -> f64; 85 | fn get_c(&self) -> String; 86 | } 87 | 88 | //Traits are also like Data Classes 89 | trait BehaviourOfShow 90 | { 91 | fn show(&self) -> String; 92 | } 93 | 94 | fn main() { 95 | } 96 | -------------------------------------------------------------------------------- /Chapter01/intro_expressions.rs: -------------------------------------------------------------------------------- 1 | struct MyStruct { 2 | a: u32, 3 | b: f32, 4 | c: String 5 | } 6 | 7 | enum Term { 8 | TermVal { value: String }, 9 | TermVar { symbol: String }, 10 | TermApp { f: Box, x: Box }, 11 | TermAbs { arg: String, body: Box } 12 | } 13 | 14 | fn main() { 15 | 16 | let x = { 17 | fn f(x: u32) -> u32 { 18 | x*x 19 | } 20 | let y = f(5); 21 | y*3 22 | }; 23 | 24 | let x; 25 | if true { 26 | x = 1; 27 | } else { 28 | x = 2; 29 | } 30 | 31 | let x = if true { 1 } else { 2 }; 32 | 33 | MyStruct { 34 | a: 1, 35 | b: 1.0, 36 | c: "".to_string() 37 | }; 38 | 39 | (1, 1.0, "".to_string()); 40 | 41 | let mut t = Term::TermVar { 42 | symbol: "".to_string() 43 | }; 44 | match t { 45 | Term::TermVal { value: v1 } => v1, 46 | Term::TermVar { symbol: v1 } => v1, 47 | Term::TermApp { f: ref v1, x: ref v2 } => "TermApp(?,?)".to_string(), 48 | Term::TermAbs { arg: ref mut v1, body: ref mut v2 } => "TermAbs(?,?)".to_string() 49 | }; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Chapter01/intro_functions.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | 3 | (0..10).map(|x| x*x); 4 | 5 | (0..10).map(|x| { 6 | 7 | fn f(y: u32) -> u32 { 8 | y*y 9 | } 10 | 11 | let z = f(x+1) * f(x+2); 12 | 13 | z*z 14 | 15 | }); 16 | 17 | fn f(g: T, x: u32) -> u32 18 | where T: Fn(u32) -> u32 19 | { 20 | g(x+1) * g(x+2) 21 | } 22 | f(|x|{x*x}, 2); 23 | 24 | (0..10).map(|x| x*x) 25 | .inspect(|x|{ println!("value {}", *x) }) 26 | .filter(|x| *x<3) 27 | .filter_map(|x| Some(x)) 28 | .fold(0, |x, y| x+y); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Chapter01/intro_generics.rs: -------------------------------------------------------------------------------- 1 | //Data Type Definitions 2 | struct PointU32 3 | { 4 | x: u32, 5 | y: u32 6 | } 7 | struct PointF32 8 | { 9 | x: f32, 10 | y: f32 11 | } 12 | 13 | struct PointI32 14 | { 15 | x: i32, 16 | y: i32 17 | } 18 | 19 | //can be written with generics 20 | struct Point 21 | { 22 | x: T, 23 | y: T 24 | } 25 | 26 | //Function Definitions 27 | fn foo_u32(x: u32) -> u32 28 | { 29 | x*x 30 | } 31 | 32 | fn foo_f32(x: f32) -> f32 33 | { 34 | x*x 35 | } 36 | 37 | fn foo_i32(x: i32) -> i32 38 | { 39 | x*x 40 | } 41 | 42 | //can be written with generics 43 | fn foo(x: T) -> T 44 | where T: std::ops::Mul + Copy 45 | { 46 | x*x 47 | } 48 | 49 | //even functions can be sent to generics 50 | //we call these "higher order functions" 51 | fn bar(f: F, x: T) -> T 52 | where F: Fn(T) -> T 53 | { 54 | f(x) 55 | } 56 | 57 | fn main() 58 | { 59 | PointU32 { x:1, y:1 }; 60 | PointF32 { x:1.0, y:1.0 }; 61 | Point { x:1, y:1 }; 62 | Point { x:1.0, y:1.0 }; 63 | foo_u32(1); 64 | foo_f32(1.0); 65 | foo(1); 66 | foo(1.0); 67 | bar(|x|{x}, 1); 68 | } 69 | -------------------------------------------------------------------------------- /Chapter01/intro_iterators.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | 3 | (0..10).chain(10..20); 4 | 5 | (0..10).zip(10..20); 6 | 7 | (0..10).enumerate(); 8 | 9 | (0..10).inspect(|x|{ println!("value {}", *x) }); 10 | 11 | (0..10).map(|x| x*x); 12 | 13 | (0..10).filter(|x| *x<3); 14 | 15 | (0..10).fold(0, |x,y| x+y); 16 | 17 | for i in (0..10) {} 18 | 19 | (0..10).collect::>(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Chapter01/intro_metaprogramming.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate metaderive; 3 | 4 | macro_rules! my_vec_macro { 5 | ( $( $x:expr ),* ) => { 6 | { 7 | let mut temp_vec = Vec::new(); 8 | $( 9 | temp_vec.push($x); 10 | )* 11 | temp_vec 12 | } 13 | }; 14 | } 15 | 16 | macro_rules! my_macro_branch 17 | { 18 | (1 $e:expr) => (println!("mode 1: {}", $e)); 19 | (2 $e:expr) => (println!("mode 2: {}", $e)); 20 | } 21 | 22 | enum DSLTerm { 23 | TVar { symbol: String }, 24 | TAbs { param: String, body: Box }, 25 | TApp { f: Box, x: Box } 26 | } 27 | 28 | macro_rules! dsl 29 | { 30 | ( ( $($e:tt)* ) ) => (dsl!( $($e)* )); 31 | ( $e:ident ) => (DSLTerm::TVar { symbol: stringify!($e).to_string() }); 32 | ( fn $p:ident . $b:tt ) => (DSLTerm::TAbs { param: stringify!($p).to_string(), body: Box::new(dsl!($b)) }); 33 | ( $f:tt $x:tt ) => (DSLTerm::TApp { f: Box::new(dsl!($f)), x: Box::new(dsl!($x)) }); 34 | } 35 | 36 | pub trait TypeName { 37 | fn typename() -> String; 38 | } 39 | 40 | #[derive(TypeName)] 41 | struct MyStructA 42 | { 43 | a: u32, 44 | b: f32 45 | } 46 | 47 | fn main() 48 | { 49 | 50 | println!("this is a macro {} {}", 1, 2); 51 | 52 | my_vec_macro!(1, 2, 3); 53 | 54 | my_macro_branch!(1 "abc"); 55 | my_macro_branch!(2 "def"); 56 | 57 | dsl!( a ); 58 | dsl!( fn a . a ); 59 | dsl!( f a ); 60 | dsl!( (f a) ); 61 | } 62 | -------------------------------------------------------------------------------- /Chapter01/intro_mixoopfp.rs: -------------------------------------------------------------------------------- 1 | struct MyObject 2 | { 3 | a: u32, 4 | b: f32, 5 | c: String 6 | } 7 | 8 | trait MyObjectTrait 9 | { 10 | fn new(a: u32, b: f32, c: String) -> Self; 11 | 12 | fn get_a(&self) -> u32; 13 | fn get_b(&self) -> f32; 14 | fn get_c(&self) -> String; 15 | } 16 | 17 | impl MyObjectTrait for MyObject 18 | { 19 | fn new(a: u32, b: f32, c: String) -> Self 20 | { 21 | MyObject { a: a, b: b, c: c } 22 | } 23 | fn get_a(&self) -> u32 24 | { 25 | self.a 26 | } 27 | fn get_b(&self) -> f32 28 | { 29 | self.b 30 | } 31 | fn get_c(&self) -> String 32 | { 33 | self.c.clone() 34 | } 35 | } 36 | 37 | trait MyObjectApply { 38 | fn apply(&self, f: T) -> R 39 | where T: Fn(u32,f32,String) -> R; 40 | } 41 | 42 | impl MyObjectApply for MyObject { 43 | fn apply(&self, f: T) -> R 44 | where T: Fn(u32,f32,String) -> R 45 | { 46 | f(self.a, self.b, self.c.clone()) 47 | } 48 | } 49 | 50 | fn main() { 51 | } 52 | -------------------------------------------------------------------------------- /Chapter01/intro_patterns.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate cached; 2 | #[macro_use] extern crate lazy_static; 3 | 4 | trait Monad { 5 | fn return_(t: A) -> Self; 6 | //:: A -> Monad 7 | 8 | fn bind(m: Self, f: Fn(A) -> MB) -> MB 9 | where MB: Monad; 10 | //:: Monad -> (A -> Monad)) -> Monad 11 | } 12 | 13 | fn not_curried(p1: u32, p2: u32) -> u32 14 | { 15 | p1 + p2 16 | } 17 | 18 | 19 | fn curried(p1: u32) -> Box u32> 20 | { 21 | Box::new(move |p2: u32| { 22 | p1 + p2 23 | }) 24 | } 25 | 26 | cached!{ 27 | FIB; 28 | fn fib(n: u64) -> u64 = { 29 | if n == 0 || n == 1 { return n } 30 | fib(n-1) + fib(n-2) 31 | } 32 | } 33 | 34 | fn main() 35 | { 36 | let fsin = |x: f64| x.sin(); 37 | let fabs = |x: f64| x.abs(); 38 | let transform = |x: f64| fabs(fsin(x)); 39 | 40 | not_curried(1, 2); 41 | curried(1)(2); 42 | 43 | let immutable_v1 = 1; 44 | //immutable_v1 = 2; //invalid 45 | let mut mutable_v2 = 1; 46 | mutable_v2 = 2; 47 | 48 | let x = { println!("side effect"); 1 + 2 }; 49 | let y = ||{ println!("side effect"); 1 + 2 }; 50 | 51 | fib(30); 52 | } 53 | -------------------------------------------------------------------------------- /Chapter01/metaderive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "metaderive" 3 | version = "1.0.0" 4 | 5 | [dependencies] 6 | syn = "0.12" 7 | quote = "0.4" 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /Chapter01/metaderive/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_type = "proc-macro"] 2 | extern crate proc_macro; 3 | extern crate syn; 4 | #[macro_use] extern crate quote; 5 | use proc_macro::TokenStream; 6 | 7 | #[proc_macro_derive(TypeName)] 8 | pub fn type_name(input: TokenStream) -> TokenStream { 9 | // Parse token stream into input AST 10 | let ast = syn::parse(input).unwrap(); 11 | 12 | // Generate output AST 13 | impl_typename(&ast).into() 14 | } 15 | 16 | fn impl_typename(ast: &syn::DeriveInput) -> quote::Tokens { 17 | let name = &ast.ident; 18 | quote! { 19 | impl TypeName for #name { 20 | fn typename() -> String { 21 | stringify!(#name).to_string() 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Chapter02/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "elevator" 3 | version = "1.0.0" 4 | 5 | [dependencies] 6 | floating-duration = "0.1.2" 7 | termion = "1.0" 8 | timebomb = "0.1" 9 | -------------------------------------------------------------------------------- /Chapter02/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate elevator; 2 | 3 | fn main() 4 | { 5 | elevator::run_simulation(); 6 | } 7 | -------------------------------------------------------------------------------- /Chapter02/test1.txt: -------------------------------------------------------------------------------- 1 | 5 2 | 5.67 3 | 2 4 | 1 5 | 4 6 | 0 7 | 3 8 | 1 9 | 0 10 | -------------------------------------------------------------------------------- /Chapter02/tests/integration_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate elevator; 2 | extern crate timebomb; 3 | use timebomb::timeout_ms; 4 | 5 | #[test] 6 | fn test_main() { 7 | timeout_ms(|| { 8 | elevator::run_simulation(); 9 | }, 300000); 10 | } 11 | -------------------------------------------------------------------------------- /Chapter03/.gitignore: -------------------------------------------------------------------------------- 1 | simulation.log 2 | -------------------------------------------------------------------------------- /Chapter03/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "elevator" 3 | version = "1.0.0" 4 | 5 | [dependencies] 6 | floating-duration = "0.1.2" 7 | termion = "1.0" 8 | timebomb = "0.1" 9 | serde = "1.0" 10 | serde_json = "1.0" 11 | serde_derive = "1.0" 12 | 13 | [[bin]] 14 | name = "elevator" 15 | path = "src/main.rs" 16 | 17 | [[bin]] 18 | name = "analyze" 19 | path = "src/analyze.rs" 20 | -------------------------------------------------------------------------------- /Chapter03/src/analyze.rs: -------------------------------------------------------------------------------- 1 | mod physics; 2 | mod motor; 3 | 4 | use physics::{ElevatorSpecification, ElevatorState, MotorInput, simulate_elevator, DataRecorder, MotorController, MotorVoltage}; 5 | 6 | #[macro_use] extern crate serde_derive; 7 | extern crate serde; 8 | extern crate serde_json; 9 | extern crate floating_duration; 10 | use std::time::Instant; 11 | use std::env; 12 | use std::fs::File; 13 | use std::io::{self, Read, Write, BufRead, BufReader}; 14 | use std::io::prelude::*; 15 | 16 | #[derive(Clone)] 17 | struct Trip { 18 | dst: u64, 19 | up: f64, 20 | down: f64 21 | } 22 | 23 | const MAX_JERK: f64 = 0.2; 24 | const MAX_ACCELERATION: f64 = 2.0; 25 | const MAX_VELOCITY: f64 = 5.0; 26 | 27 | fn main() 28 | { 29 | let simlog = File::open("simulation.log").expect("read simulation log"); 30 | let mut simlog = BufReader::new(&simlog); 31 | let mut jerk = 0.0; 32 | let mut prev_est: Option = None; 33 | let mut dst_timing: Vec = Vec::new(); 34 | let mut start_location = 0.0; 35 | 36 | let mut first_line = String::new(); 37 | let len = simlog.read_line(&mut first_line).unwrap(); 38 | let esp: ElevatorSpecification = serde_json::from_str(&first_line).unwrap(); 39 | 40 | for line in simlog.lines() { 41 | let l = line.unwrap(); 42 | let (est, dst): (ElevatorState,u64) = serde_json::from_str(&l).unwrap(); 43 | let dl = dst_timing.len(); 44 | if dst_timing.len()==0 || dst_timing[dl-1].dst != dst { 45 | dst_timing.push(Trip { dst:dst, up:0.0, down:0.0 }); 46 | } 47 | 48 | if let Some(prev_est) = prev_est { 49 | let dt = est.timestamp - prev_est.timestamp; 50 | if est.velocity > 0.0 { 51 | dst_timing[dl-1].up += dt; 52 | } else { 53 | dst_timing[dl-1].down += dt; 54 | } 55 | let da = (est.acceleration - prev_est.acceleration).abs(); 56 | jerk = (jerk * (1.0 - dt)) + (da * dt); 57 | if jerk.abs() > 0.22 { 58 | panic!("jerk is outside of acceptable limits: {} {:?}", jerk, est) 59 | } 60 | } else { 61 | start_location = est.location; 62 | } 63 | if est.acceleration.abs() > 2.2 { 64 | panic!("acceleration is outside of acceptable limits: {:?}", est) 65 | } 66 | if est.velocity.abs() > 5.5 { 67 | panic!("velocity is outside of acceptable limits: {:?}", est) 68 | } 69 | prev_est = Some(est); 70 | } 71 | 72 | //elevator should not backup 73 | let mut total_time = 0.0; 74 | let mut total_direct = 0.0; 75 | for trip in dst_timing.clone() 76 | { 77 | total_time += (trip.up + trip.down); 78 | if trip.up > trip.down { 79 | total_direct += trip.up; 80 | } else { 81 | total_direct += trip.down; 82 | } 83 | } 84 | if (total_direct / total_time) < 0.9 { 85 | panic!("elevator back up is too common: {}", total_direct / total_time) 86 | } 87 | 88 | 89 | //trips should finish within 20% of theoretical limit 90 | let mut trip_start_location = start_location; 91 | let mut theoretical_time = 0.0; 92 | let floor_height = esp.floor_height; 93 | for trip in dst_timing.clone() 94 | { 95 | let next_floor = (trip.dst as f64) * floor_height; 96 | let d = (trip_start_location - next_floor).abs(); 97 | theoretical_time += ( 98 | 2.0*(MAX_ACCELERATION / MAX_JERK) + 99 | 2.0*(MAX_JERK / MAX_ACCELERATION) + 100 | d / MAX_VELOCITY 101 | ); 102 | trip_start_location = next_floor; 103 | } 104 | if total_time > (theoretical_time * 1.2) { 105 | panic!("elevator moves to slow {} {}", total_time, theoretical_time * 1.2) 106 | } 107 | 108 | println!("All simulation checks passing."); 109 | } 110 | -------------------------------------------------------------------------------- /Chapter03/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod physics; 2 | mod motor; 3 | 4 | use physics::{ElevatorSpecification, ElevatorState, MotorInput, simulate_elevator, DataRecorder, MotorController, MotorVoltage}; 5 | use motor::{SmoothMotorController, SimpleMotorController}; 6 | 7 | #[macro_use] extern crate serde_derive; 8 | extern crate serde; 9 | extern crate serde_json; 10 | extern crate floating_duration; 11 | use std::time::Instant; 12 | use std::env; 13 | use std::fs::File; 14 | use std::io::{self, Read, Write}; 15 | use std::io::prelude::*; 16 | extern crate termion; 17 | use termion::{clear, cursor, style}; 18 | use termion::raw; 19 | use termion::raw::IntoRawMode; 20 | use termion::input::TermRead; 21 | use termion::event::Key; 22 | use std::cmp; 23 | 24 | fn variable_summary(stdout: &mut raw::RawTerminal, vname: String, data: &Vec) { 25 | let (avg, dev) = variable_summary_stats(data); 26 | variable_summary_print(stdout, vname, avg, dev); 27 | } 28 | 29 | fn variable_summary_stats(data: &Vec) -> (f64, f64) 30 | { 31 | //calculate statistics 32 | let N = data.len(); 33 | let sum = data.clone().into_iter() 34 | .fold(0.0, |a, b| a+b); 35 | let avg = sum / (N as f64); 36 | let dev = ( 37 | data.clone().into_iter() 38 | .map(|v| (v - avg).powi(2)) 39 | .fold(0.0, |a, b| a+b) 40 | / (N as f64) 41 | ).sqrt(); 42 | (avg, dev) 43 | } 44 | 45 | fn variable_summary_print(stdout: &mut raw::RawTerminal, vname: String, avg: f64, dev: f64) 46 | { 47 | //print formatted output 48 | write!(stdout, "Average of {:25}{:.6}\r\n", vname, avg); 49 | write!(stdout, "Standard deviation of {:14}{:.6}\r\n", vname, dev); 50 | write!(stdout, "\r\n"); 51 | } 52 | 53 | struct SimpleDataRecorder<'a, W: 'a + Write> 54 | { 55 | esp: ElevatorSpecification, 56 | termwidth: u64, 57 | termheight: u64, 58 | stdout: &'a mut raw::RawTerminal, 59 | log: File, 60 | record_location: Vec, 61 | record_velocity: Vec, 62 | record_acceleration: Vec, 63 | record_voltage: Vec, 64 | } 65 | impl<'a, W: Write> DataRecorder for SimpleDataRecorder<'a, W> 66 | { 67 | fn init(&mut self, esp: ElevatorSpecification, est: ElevatorState) 68 | { 69 | self.esp = esp.clone(); 70 | self.log.write_all(serde_json::to_string(&esp).unwrap().as_bytes()).expect("write spec to log"); 71 | self.log.write_all(b"\r\n").expect("write spec to log"); 72 | } 73 | fn poll(&mut self, est: ElevatorState, dst: u64) 74 | { 75 | let datum = (est.clone(), dst); 76 | self.log.write_all(serde_json::to_string(&datum).unwrap().as_bytes()).expect("write state to log"); 77 | self.log.write_all(b"\r\n").expect("write state to log"); 78 | 79 | self.record_location.push(est.location); 80 | self.record_velocity.push(est.velocity); 81 | self.record_acceleration.push(est.acceleration); 82 | self.record_voltage.push(est.motor_input.voltage()); 83 | 84 | //5.4. Print realtime statistics 85 | print!("{}{}{}", clear::All, cursor::Goto(1, 1), cursor::Hide); 86 | let carriage_floor = (est.location / self.esp.floor_height).floor(); 87 | let carriage_floor = if carriage_floor < 1.0 { 0 } else { carriage_floor as u64 }; 88 | let carriage_floor = cmp::min(carriage_floor, self.esp.floor_count-1); 89 | let mut terminal_buffer = vec![' ' as u8; (self.termwidth*self.termheight) as usize]; 90 | for ty in 0..self.esp.floor_count 91 | { 92 | terminal_buffer[ (ty*self.termwidth + 0) as usize ] = '[' as u8; 93 | terminal_buffer[ (ty*self.termwidth + 1) as usize ] = 94 | if (ty as u64)==((self.esp.floor_count-1)-carriage_floor) { 'X' as u8 } 95 | else { ' ' as u8 }; 96 | terminal_buffer[ (ty*self.termwidth + 2) as usize ] = ']' as u8; 97 | terminal_buffer[ (ty*self.termwidth + self.termwidth-2) as usize ] = '\r' as u8; 98 | terminal_buffer[ (ty*self.termwidth + self.termwidth-1) as usize ] = '\n' as u8; 99 | } 100 | let stats = vec![ 101 | format!("Carriage at floor {}", carriage_floor+1), 102 | format!("Location {:.06}", est.location), 103 | format!("Velocity {:.06}", est.velocity), 104 | format!("Acceleration {:.06}", est.acceleration), 105 | format!("Voltage [up-down] {:.06}", est.motor_input.voltage()), 106 | ]; 107 | for sy in 0..stats.len() 108 | { 109 | for (sx,sc) in stats[sy].chars().enumerate() 110 | { 111 | terminal_buffer[ sy*(self.termwidth as usize) + 6 + sx ] = sc as u8; 112 | } 113 | } 114 | write!(self.stdout, "{}", String::from_utf8(terminal_buffer).ok().unwrap()); 115 | self.stdout.flush().unwrap(); 116 | } 117 | fn summary(&mut self) 118 | { 119 | //6 Calculate and print summary statistics 120 | write!(self.stdout, "{}{}{}", clear::All, cursor::Goto(1, 1), cursor::Show).unwrap(); 121 | variable_summary(&mut self.stdout, "location".to_string(), &self.record_location); 122 | variable_summary(&mut self.stdout, "velocity".to_string(), &self.record_velocity); 123 | variable_summary(&mut self.stdout, "acceleration".to_string(), &self.record_acceleration); 124 | variable_summary(&mut self.stdout, "voltage".to_string(), &self.record_voltage); 125 | self.stdout.flush().unwrap(); 126 | } 127 | } 128 | 129 | pub fn run_simulation() 130 | { 131 | 132 | //1. Store location, velocity, and acceleration state 133 | //2. Store motor input voltage 134 | let mut est = ElevatorState { 135 | timestamp: 0.0, 136 | location: 0.0, 137 | velocity: 0.0, 138 | acceleration: 0.0, 139 | motor_input: MotorInput::Up { 140 | //zero is positive force to counter gravity 141 | voltage: 9.8 * (120000.0 / 8.0) 142 | } 143 | }; 144 | 145 | //3. Store input building description and floor requests 146 | let mut esp = ElevatorSpecification { 147 | floor_count: 0, 148 | floor_height: 0.0, 149 | carriage_weight: 120000.0 150 | }; 151 | let mut floor_requests = Vec::new(); 152 | 153 | //4. Parse input and store as building description and floor requests 154 | let buffer = match env::args().nth(1) { 155 | Some(ref fp) if *fp == "-".to_string() => { 156 | let mut buffer = String::new(); 157 | io::stdin().read_to_string(&mut buffer) 158 | .expect("read_to_string failed"); 159 | buffer 160 | }, 161 | None => { 162 | let fp = "test1.txt"; 163 | let mut buffer = String::new(); 164 | File::open(fp) 165 | .expect("File::open failed") 166 | .read_to_string(&mut buffer) 167 | .expect("read_to_string failed"); 168 | buffer 169 | }, 170 | Some(fp) => { 171 | let mut buffer = String::new(); 172 | File::open(fp) 173 | .expect("File::open failed") 174 | .read_to_string(&mut buffer) 175 | .expect("read_to_string failed"); 176 | buffer 177 | } 178 | }; 179 | 180 | for (li,l) in buffer.lines().enumerate() { 181 | if li==0 { 182 | esp.floor_count = l.parse::().unwrap(); 183 | } else if li==1 { 184 | esp.floor_height = l.parse::().unwrap(); 185 | } else { 186 | floor_requests.push(l.parse::().unwrap()); 187 | } 188 | } 189 | 190 | let termsize = termion::terminal_size().ok(); 191 | let mut dr = SimpleDataRecorder { 192 | esp: esp.clone(), 193 | termwidth: termsize.map(|(w,_)| w-2).expect("termwidth") as u64, 194 | termheight: termsize.map(|(_,h)| h-2).expect("termheight") as u64, 195 | stdout: &mut io::stdout().into_raw_mode().unwrap(), 196 | log: File::create("simulation.log").expect("log file"), 197 | record_location: Vec::new(), 198 | record_velocity: Vec::new(), 199 | record_acceleration: Vec::new(), 200 | record_voltage: Vec::new() 201 | }; 202 | /* 203 | let mut mc = SimpleMotorController { 204 | esp: esp.clone() 205 | }; 206 | */ 207 | let mut mc = SmoothMotorController { 208 | timestamp: 0.0, 209 | esp: esp.clone() 210 | }; 211 | 212 | simulate_elevator(esp, est, floor_requests, &mut mc, &mut dr); 213 | dr.summary(); 214 | 215 | } 216 | 217 | #[cfg(test)] 218 | mod tests { 219 | use super::*; 220 | 221 | #[test] 222 | fn variable_stats() { 223 | let test_data = vec![ 224 | (vec![1.0, 2.0, 3.0, 4.0, 5.0], 3.0, 1.41), 225 | (vec![1.0, 3.0, 5.0, 7.0, 9.0], 5.0, 2.83), 226 | (vec![1.0, 9.0, 1.0, 9.0, 1.0], 4.2, 3.92), 227 | (vec![1.0, 0.5, 0.7, 0.9, 0.6], 0.74, 0.19), 228 | (vec![200.0, 3.0, 24.0, 92.0, 111.0], 86.0, 69.84), 229 | ]; 230 | for (data, avg, dev) in test_data 231 | { 232 | let (ravg, rdev) = variable_summary_stats(data); 233 | assert!( (avg-ravg).abs() < 0.1 ); 234 | assert!( (dev-rdev).abs() < 0.1 ); 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /Chapter03/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate elevator; 2 | 3 | fn main() 4 | { 5 | elevator::run_simulation(); 6 | } 7 | -------------------------------------------------------------------------------- /Chapter03/src/motor.rs: -------------------------------------------------------------------------------- 1 | extern crate floating_duration; 2 | use std::time::Instant; 3 | use floating_duration::{TimeAsFloat, TimeFormat}; 4 | use physics::{ElevatorSpecification, ElevatorState, MotorInput, MotorController}; 5 | 6 | pub struct SimpleMotorController 7 | { 8 | pub esp: ElevatorSpecification 9 | } 10 | 11 | impl MotorController for SimpleMotorController 12 | { 13 | fn init(&mut self, esp: ElevatorSpecification, est: ElevatorState) 14 | { 15 | self.esp = esp; 16 | } 17 | 18 | fn poll(&mut self, est: ElevatorState, dst: u64) -> MotorInput 19 | { 20 | //5.3. Adjust motor control to process next floor request 21 | 22 | //it will take t seconds to decelerate from velocity v at -1 m/s^2 23 | let t = est.velocity.abs() / 1.0; 24 | 25 | //during which time, the carriage will travel d=t * v/2 meters 26 | //at an average velocity of v/2 before stopping 27 | let d = t * (est.velocity/2.0); 28 | 29 | let dst_height = (dst as f64) * self.esp.floor_height; 30 | 31 | //l = distance to next floor 32 | let l = (est.location - dst_height).abs(); 33 | 34 | let target_acceleration = { 35 | //are we going up? 36 | let going_up = est.location < dst_height; 37 | 38 | //Do not exceed maximum velocity 39 | if est.velocity.abs() >= 5.0 { 40 | if (going_up && est.velocity>0.0) 41 | || (!going_up && est.velocity<0.0) { 42 | 0.0 43 | //decelerate if going in wrong direction 44 | } else if going_up { 45 | 1.0 46 | } else { 47 | -1.0 48 | } 49 | 50 | //if within comfortable deceleration range and moving in right direction, decelerate 51 | } else if l < d && ((going_up && est.velocity>0.0) 52 | || (!going_up && est.velocity<0.0)) { 53 | if going_up { 54 | -1.0 55 | } else { 56 | 1.0 57 | } 58 | 59 | //else if not at peak velocity, accelerate 60 | } else { 61 | if going_up { 62 | 1.0 63 | } else { 64 | -1.0 65 | } 66 | } 67 | }; 68 | 69 | let gravity_adjusted_acceleration = target_acceleration + 9.8; 70 | let target_force = gravity_adjusted_acceleration * self.esp.carriage_weight; 71 | let target_voltage = target_force / 8.0; 72 | if target_voltage > 0.0 { 73 | MotorInput::Up { voltage: target_voltage } 74 | } else { 75 | MotorInput::Down { voltage: target_voltage.abs() } 76 | } 77 | } 78 | } 79 | 80 | const MAX_JERK: f64 = 0.2; 81 | const MAX_ACCELERATION: f64 = 2.0; 82 | const MAX_VELOCITY: f64 = 5.0; 83 | 84 | pub struct SmoothMotorController 85 | { 86 | pub esp: ElevatorSpecification, 87 | pub timestamp: f64 88 | } 89 | 90 | impl MotorController for SmoothMotorController 91 | { 92 | fn init(&mut self, esp: ElevatorSpecification, est: ElevatorState) 93 | { 94 | self.esp = esp; 95 | self.timestamp = est.timestamp; 96 | } 97 | 98 | fn poll(&mut self, est: ElevatorState, dst: u64) -> MotorInput 99 | { 100 | //5.3. Adjust motor control to process next floor request 101 | 102 | //it will take t seconds to reach max from max 103 | let t_accel = MAX_ACCELERATION / MAX_JERK; 104 | let t_veloc = MAX_VELOCITY / MAX_ACCELERATION; 105 | 106 | //it may take up to d meters to decelerate from current 107 | let decel_t = if (est.velocity>0.0) == (est.acceleration>0.0) { 108 | //this case deliberately overestimates d to prevent "back up" 109 | (est.acceleration.abs() / MAX_JERK) + 110 | (est.velocity.abs() / (MAX_ACCELERATION / 2.0)) + 111 | 2.0 * (MAX_ACCELERATION / MAX_JERK) 112 | } else { 113 | //without the MAX_JERK, this approaches infinity and decelerates way too soon 114 | //MAX_JERK * 1s = acceleration in m/s^2 115 | est.velocity.abs() / (MAX_JERK + est.acceleration.abs()) 116 | }; 117 | let d = est.velocity.abs() * decel_t; 118 | 119 | //l = distance to next floor 120 | let l = (est.location - (dst as f64)*self.esp.floor_height).abs(); 121 | 122 | let target_acceleration = { 123 | //are we going up? 124 | let going_up = est.location < (dst as f64)*self.esp.floor_height; 125 | 126 | //time elapsed since last poll 127 | let dt = est.timestamp - self.timestamp; 128 | self.timestamp = est.timestamp; 129 | 130 | //Do not exceed maximum acceleration 131 | if est.acceleration.abs() >= MAX_ACCELERATION { 132 | if est.acceleration > 0.0 { 133 | est.acceleration - (dt * MAX_JERK) 134 | } else { 135 | est.acceleration + (dt * MAX_JERK) 136 | } 137 | 138 | //Do not exceed maximum velocity 139 | } else if est.velocity.abs() >= MAX_VELOCITY 140 | || (est.velocity + est.acceleration * (est.acceleration.abs() / MAX_JERK)).abs() >= MAX_VELOCITY { 141 | if est.velocity > 0.0 { 142 | est.acceleration - (dt * MAX_JERK) 143 | } else { 144 | est.acceleration + (dt * MAX_JERK) 145 | } 146 | 147 | //if within comfortable deceleration range and moving in right direction, decelerate 148 | } else if l < d && (est.velocity>0.0) == going_up { 149 | if going_up { 150 | est.acceleration - (dt * MAX_JERK) 151 | } else { 152 | est.acceleration + (dt * MAX_JERK) 153 | } 154 | 155 | //else if not at peak velocity, accelerate smoothly 156 | } else { 157 | if going_up { 158 | est.acceleration + (dt * MAX_JERK) 159 | } else { 160 | est.acceleration - (dt * MAX_JERK) 161 | } 162 | } 163 | }; 164 | 165 | let gravity_adjusted_acceleration = target_acceleration + 9.8; 166 | let target_force = gravity_adjusted_acceleration * self.esp.carriage_weight; 167 | let target_voltage = target_force / 8.0; 168 | if !target_voltage.is_finite() { 169 | //divide by zero etc. 170 | //may happen if time delta underflows 171 | MotorInput::Up { voltage: 0.0 } 172 | } else if target_voltage > 0.0 { 173 | MotorInput::Up { voltage: target_voltage } 174 | } else { 175 | MotorInput::Down { voltage: target_voltage.abs() } 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /Chapter03/src/physics.rs: -------------------------------------------------------------------------------- 1 | extern crate floating_duration; 2 | use std::time::Instant; 3 | use floating_duration::{TimeAsFloat, TimeFormat}; 4 | use std::{thread, time}; 5 | 6 | #[derive(Clone,Serialize,Deserialize,Debug)] 7 | pub enum MotorInput 8 | { 9 | Up { voltage: f64 }, 10 | Down { voltage: f64 } 11 | } 12 | 13 | #[derive(Clone,Serialize,Deserialize,Debug)] 14 | pub struct ElevatorSpecification 15 | { 16 | pub floor_count: u64, 17 | pub floor_height: f64, 18 | pub carriage_weight: f64 19 | } 20 | 21 | #[derive(Clone,Serialize,Deserialize,Debug)] 22 | pub struct ElevatorState 23 | { 24 | pub timestamp: f64, 25 | pub location: f64, 26 | pub velocity: f64, 27 | pub acceleration: f64, 28 | pub motor_input: MotorInput 29 | } 30 | 31 | pub type FloorRequests = Vec; 32 | 33 | pub trait MotorController 34 | { 35 | fn init(&mut self, esp: ElevatorSpecification, est: ElevatorState); 36 | fn poll(&mut self, est: ElevatorState, dst: u64) -> MotorInput; 37 | } 38 | 39 | pub trait DataRecorder 40 | { 41 | fn init(&mut self, esp: ElevatorSpecification, est: ElevatorState); 42 | fn poll(&mut self, est: ElevatorState, dst: u64); 43 | fn summary(&mut self); 44 | } 45 | 46 | pub trait MotorForce { 47 | fn calculate_force(&self) -> f64; 48 | } 49 | impl MotorForce for MotorInput { 50 | fn calculate_force(&self) -> f64 51 | { 52 | match *self { 53 | MotorInput::Up { voltage: v } => { v * 8.0 } 54 | MotorInput::Down { voltage: v } => { v * -8.0 } 55 | } 56 | } 57 | } 58 | 59 | pub trait MotorVoltage { 60 | fn voltage(&self) -> f64; 61 | } 62 | impl MotorVoltage for MotorInput { 63 | fn voltage(&self) -> f64 64 | { 65 | match *self { 66 | MotorInput::Up { voltage: v } => { v } 67 | MotorInput::Down { voltage: v } => { -v } 68 | } 69 | } 70 | } 71 | 72 | pub fn simulate_elevator(esp: ElevatorSpecification, est: ElevatorState, req: FloorRequests, 73 | mc: &mut MC, dr: &mut DR) { 74 | 75 | //immutable input becomes mutable local state 76 | let mut esp = esp.clone(); 77 | let mut est = est.clone(); 78 | let mut req = req.clone(); 79 | 80 | //initialize MotorController and DataController 81 | mc.init(esp.clone(), est.clone()); 82 | dr.init(esp.clone(), est.clone()); 83 | 84 | //5. Loop while there are remaining floor requests 85 | let original_ts = Instant::now(); 86 | thread::sleep(time::Duration::from_millis(1)); 87 | while req.len() > 0 88 | { 89 | //5.1. Update location, velocity, and acceleration 90 | let now = Instant::now(); 91 | let ts = now.duration_since(original_ts) 92 | .as_fractional_secs(); 93 | let dt = ts - est.timestamp; 94 | est.timestamp = ts; 95 | 96 | est.location = est.location + est.velocity * dt; 97 | est.velocity = est.velocity + est.acceleration * dt; 98 | est.acceleration = { 99 | let F = est.motor_input.calculate_force(); 100 | let m = esp.carriage_weight; 101 | -9.8 + F/m 102 | }; 103 | 104 | //5.2. If next floor request in queue is satisfied, then remove from queue 105 | let next_floor = req[0]; 106 | if (est.location - (next_floor as f64)*esp.floor_height).abs() < 0.01 && 107 | est.velocity.abs() < 0.01 108 | { 109 | est.velocity = 0.0; 110 | req.remove(0); 111 | } 112 | 113 | //5.4. Print realtime statistics 114 | dr.poll(est.clone(), next_floor); 115 | 116 | //5.3. Adjust motor control to process next floor request 117 | est.motor_input = mc.poll(est.clone(), next_floor); 118 | 119 | thread::sleep(time::Duration::from_millis(1)); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Chapter03/test1.txt: -------------------------------------------------------------------------------- 1 | 5 2 | 5.67 3 | 2 4 | 1 5 | 4 6 | 0 7 | 3 8 | 1 9 | 0 10 | -------------------------------------------------------------------------------- /Chapter03/tests/integration_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate elevator; 2 | extern crate timebomb; 3 | use timebomb::timeout_ms; 4 | 5 | #[test] 6 | fn test_main() { 7 | timeout_ms(|| { 8 | elevator::run_simulation(); 9 | }, 300000); 10 | } 11 | -------------------------------------------------------------------------------- /Chapter04/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "elevator" 3 | version = "1.0.0" 4 | 5 | [dependencies] 6 | floating-duration = "0.1.2" 7 | termion = "1.0" 8 | timebomb = "0.1" 9 | serde = "1.0" 10 | serde_json = "1.0" 11 | serde_derive = "1.0" 12 | 13 | [[bin]] 14 | name = "elevator" 15 | path = "src/main.rs" 16 | 17 | [[bin]] 18 | name = "analyze" 19 | path = "src/analyze.rs" 20 | 21 | [[bin]] 22 | name = "generics" 23 | path = "generics.rs" 24 | 25 | [[bin]] 26 | name = "polymorphism" 27 | path = "polymorphism.rs" 28 | 29 | [[bin]] 30 | name = "gadt" 31 | path = "gadt.rs" 32 | 33 | [[bin]] 34 | name = "lifetimes" 35 | path = "lifetimes.rs" 36 | 37 | [[bin]] 38 | name = "parameters" 39 | path = "parameters.rs" 40 | 41 | [[bin]] 42 | name = "serialize" 43 | path = "serialize.rs" 44 | -------------------------------------------------------------------------------- /Chapter04/gadt.rs: -------------------------------------------------------------------------------- 1 | 2 | struct JSJIT(u64); 3 | 4 | enum JSJITorExpr { 5 | Jit { label: JSJIT }, 6 | Expr { expr: Box } 7 | } 8 | 9 | enum JSExpr { 10 | Integer { value: u64 }, 11 | String { value: String }, 12 | OperatorAdd { lexpr: Box, rexpr: Box }, 13 | OperatorMul { lexpr: Box, rexpr: Box } 14 | } 15 | 16 | fn jump(l: JSJIT) -> JSJITorExpr 17 | { 18 | //jump to compiled code 19 | //this depends on implementation 20 | //so we will just leave this as a stub 21 | JSJITorExpr::Jit { label: JSJIT(0) } 22 | } 23 | fn eval(e: JSJITorExpr) -> JSJITorExpr 24 | { 25 | match e 26 | { 27 | JSJITorExpr::Jit { label: label } => jump(label), 28 | JSJITorExpr::Expr { expr: expr } => { 29 | let rawexpr = *expr; 30 | match rawexpr 31 | { 32 | JSExpr::Integer {..} => JSJITorExpr::Expr { expr: Box::new(rawexpr) }, 33 | JSExpr::String {..} => JSJITorExpr::Expr { expr: Box::new(rawexpr) }, 34 | JSExpr::OperatorAdd { lexpr: l, rexpr: r } => { 35 | let l = eval(*l); 36 | let r = eval(*r); 37 | //call add op codes for possible l,r representations 38 | //should return wrapped value from above 39 | JSJITorExpr::Jit { label: JSJIT(0) } 40 | } 41 | JSExpr::OperatorMul { lexpr: l, rexpr: r } => { 42 | let l = eval(*l); 43 | let r = eval(*r); 44 | //call mul op codes for possible l,r representations 45 | //should return wrapped value from above 46 | JSJITorExpr::Jit { label: JSJIT(0) } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | pub trait HList: Sized {} 54 | 55 | pub struct HNil; 56 | impl HList for HNil {} 57 | 58 | pub struct HCons { 59 | pub head: H, 60 | pub tail: T, 61 | } 62 | impl HList for HCons {} 63 | impl HCons { 64 | pub fn pop(self) -> (H, T) { 65 | (self.head, self.tail) 66 | } 67 | } 68 | 69 | fn main() 70 | { 71 | let hl = HCons { 72 | head: 2, 73 | tail: HCons { 74 | head: "abcd".to_string(), 75 | tail: HNil 76 | } 77 | }; 78 | let (h1,t1) = hl.pop(); 79 | let (h2,t2) = t1.pop(); 80 | //this would fail 81 | //HNil has no .pop method 82 | //t2.pop(); 83 | 84 | } 85 | -------------------------------------------------------------------------------- /Chapter04/generics.rs: -------------------------------------------------------------------------------- 1 | struct Cat 2 | { 3 | weight: f64, 4 | speed: f64 5 | } 6 | 7 | struct Dog 8 | { 9 | weight: f64, 10 | speed: f64 11 | } 12 | 13 | trait Animal 14 | { 15 | fn max_speed(&self) -> f64; 16 | } 17 | 18 | impl Animal for Cat 19 | { 20 | fn max_speed(&self) -> f64 21 | { 22 | self.speed 23 | } 24 | } 25 | 26 | impl Animal for Dog 27 | { 28 | fn max_speed(&self) -> f64 29 | { 30 | self.speed 31 | } 32 | } 33 | 34 | struct SqueakyToy 35 | { 36 | weight: f64 37 | } 38 | 39 | struct Stick 40 | { 41 | weight: f64 42 | } 43 | 44 | trait Toy 45 | { 46 | fn weight(&self) -> f64; 47 | } 48 | 49 | impl Toy for SqueakyToy 50 | { 51 | fn weight(&self) -> f64 52 | { 53 | self.weight 54 | } 55 | } 56 | 57 | impl Toy for Stick 58 | { 59 | fn weight(&self) -> f64 60 | { 61 | self.weight 62 | } 63 | } 64 | 65 | struct AnimalChasingToy 66 | { 67 | animal: A, 68 | toy: T 69 | } 70 | 71 | trait AnimalChasesToy 72 | { 73 | fn chase(&self); 74 | } 75 | 76 | impl AnimalChasesToy for AnimalChasingToy 77 | { 78 | fn chase(&self) 79 | { 80 | println!("chase") 81 | } 82 | } 83 | 84 | fn main() 85 | { 86 | } 87 | -------------------------------------------------------------------------------- /Chapter04/lifetimes.rs: -------------------------------------------------------------------------------- 1 | fn ground_lifetime<'a>(x: &'a u64) -> &'a u64 2 | { 3 | x 4 | } 5 | 6 | struct Ref<'a, T: 'a>(&'a T); 7 | 8 | trait Red { } 9 | 10 | struct Ball<'a> { 11 | diameter: &'a i32, 12 | } 13 | 14 | impl<'a> Red for Ball<'a> { } 15 | 16 | static num: i32 = 5; 17 | 18 | struct Context<'s>(&'s mut String); 19 | 20 | impl<'s> Context<'s> 21 | { 22 | fn mutate<'c>(&mut self, cs: &'c mut String) -> &'c mut String 23 | { 24 | let swap_a = self.0.pop().unwrap(); 25 | let swap_b = cs.pop().unwrap(); 26 | self.0.push(swap_b); 27 | cs.push(swap_a); 28 | cs 29 | } 30 | } 31 | 32 | fn main() 33 | { 34 | let x = 3; 35 | ground_lifetime(&x); 36 | 37 | let obj = Box::new(Ball { diameter: &num }) as Box; 38 | 39 | let mut s = "outside string context abc".to_string(); 40 | { 41 | //temporary context 42 | let mut c = Context(&mut s); 43 | { 44 | //further temporary context 45 | let mut s2 = "inside string context def".to_string(); 46 | c.mutate(&mut s2); 47 | println!("s2 {}", s2); 48 | } 49 | } 50 | println!("s {}", s); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Chapter04/parameters.rs: -------------------------------------------------------------------------------- 1 | type TFoo<'a, A: 'a> = (&'a A, u64); 2 | 3 | struct SFoo<'a, A: 'a>(&'a A); 4 | 5 | struct SBar<'a, A: 'a> 6 | { 7 | x: &'a A 8 | } 9 | 10 | enum EFoo<'a, A: 'a> 11 | { 12 | X { x: &'a A }, 13 | Y { y: &'a A }, 14 | } 15 | 16 | struct SBaz<'a, 'b, A: 'a, B: 'b> 17 | { 18 | a: &'a A, 19 | b: &'b B, 20 | } 21 | 22 | trait TBaz<'a, 'b, A: 'a, B: 'b> 23 | { 24 | fn baz(&self); 25 | } 26 | 27 | impl<'a, 'b, A: 'a, B: 'b> TBaz<'a, 'b, A, B> for SBaz<'a, 'b, A, B> 28 | { 29 | fn baz(&self){} 30 | } 31 | 32 | trait Foo { 33 | fn f(&self); 34 | } 35 | 36 | trait Bar { 37 | fn f(&self); 38 | } 39 | 40 | struct Baz; 41 | 42 | impl Foo for Baz { 43 | fn f(&self) { println!("Baz’s impl of Foo"); } 44 | } 45 | 46 | impl Bar for Baz { 47 | fn f(&self) { println!("Baz’s impl of Bar"); } 48 | } 49 | 50 | fn main() 51 | { 52 | let b = Baz; 53 | 54 | Foo::f(&b); 55 | Bar::f(&b); 56 | 57 | ::f(&b); 58 | ::f(&b); 59 | } 60 | -------------------------------------------------------------------------------- /Chapter04/polymorphism.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Mul; 2 | 3 | fn raise_by_three(x: T) -> T 4 | where T: std::ops::Mul 5 | { 6 | x * x * x 7 | } 8 | 9 | #[derive(Copy, Clone)] 10 | struct Raiseable 11 | { 12 | x: T 13 | } 14 | impl std::ops::Mul for Raiseable 15 | where T: std::ops::Mul 16 | { 17 | type Output = Raiseable; 18 | fn mul(self, rhs: Self) -> Self::Output 19 | { 20 | Raiseable { x: self.x * rhs.x } 21 | } 22 | } 23 | 24 | fn foo(x: X) -> X 25 | { 26 | x 27 | } 28 | 29 | fn bar(f: fn(X) -> X, x: X) -> X 30 | { 31 | f(x) 32 | } 33 | 34 | fn baz(f: F, x: X) -> X 35 | where F: Fn(X) -> X 36 | { 37 | f(x) 38 | } 39 | 40 | fn main() 41 | { 42 | 43 | raise_by_three(10); 44 | (10 as u64).pow(3); 45 | 46 | raise_by_three(3.0); 47 | (3.0 as f64).powi(3); 48 | 49 | let x = Raiseable { x: 10 as u64 }; 50 | raise_by_three(x); 51 | //no method named pow 52 | //x.pow(3); 53 | 54 | let x = Raiseable { x: 3.0 as f64 }; 55 | raise_by_three(x); 56 | //no method named powi 57 | //x.powi(3); 58 | 59 | foo(1); 60 | bar(foo,1); 61 | 62 | baz(|x| x, 1); 63 | baz(foo, 1); 64 | } 65 | -------------------------------------------------------------------------------- /Chapter04/serialize.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate serde_derive; 2 | extern crate serde; 3 | extern crate serde_json; 4 | use serde::{Serialize, Serializer, Deserialize, Deserializer}; 5 | use std::fmt; 6 | use serde::de::{self, Visitor}; 7 | 8 | struct Foo 9 | { 10 | bar: Box 11 | } 12 | 13 | #[derive(Clone,Serialize,Deserialize)] 14 | enum T_Enum 15 | { 16 | S1(S1), 17 | S2(S2), 18 | } 19 | 20 | trait T { 21 | fn as_enum(&self) -> T_Enum; 22 | } 23 | 24 | #[derive(Clone,Serialize,Deserialize)] 25 | struct S1; 26 | impl T for S1 { 27 | fn as_enum(&self) -> T_Enum 28 | { 29 | T_Enum::S1(self.clone()) 30 | } 31 | } 32 | 33 | #[derive(Clone,Serialize,Deserialize)] 34 | struct S2; 35 | impl T for S2 { 36 | fn as_enum(&self) -> T_Enum 37 | { 38 | T_Enum::S2(self.clone()) 39 | } 40 | } 41 | 42 | #[derive(Serialize,Deserialize)] 43 | struct Container 44 | { 45 | field: Box 46 | } 47 | 48 | impl Serialize for Box { 49 | fn serialize(&self, serializer: S) -> Result 50 | where S: Serializer 51 | { 52 | self.as_enum().serialize(serializer) 53 | } 54 | } 55 | 56 | impl<'de> Deserialize<'de> for Box { 57 | fn deserialize(deserializer: D) -> Result, D::Error> 58 | where D: Deserializer<'de> 59 | { 60 | let result = T_Enum::deserialize(deserializer); 61 | match result 62 | { 63 | Result::Ok(te) => { 64 | match te { 65 | T_Enum::S1(s1) => Result::Ok(Box::new(s1.clone())), 66 | T_Enum::S2(s2) => Result::Ok(Box::new(s2.clone())) 67 | } 68 | } 69 | Result::Err(err) => Result::Err(err) 70 | } 71 | } 72 | } 73 | 74 | fn main() 75 | { 76 | let bt: Box = Box::new(S1); 77 | let s = serde_json::to_string(&bt).unwrap(); 78 | let bt: Box = serde_json::from_str(s.as_str()).unwrap(); 79 | } 80 | -------------------------------------------------------------------------------- /Chapter04/src/analyze.rs: -------------------------------------------------------------------------------- 1 | mod physics; 2 | mod motor; 3 | 4 | use physics::{ElevatorSpecification, ElevatorState, MotorInput, simulate_elevator, DataRecorder, MotorController, MotorVoltage, 5 | ElevatorStateClone, ElevatorSpecificationClone}; 6 | 7 | #[macro_use] extern crate serde_derive; 8 | extern crate serde; 9 | extern crate serde_json; 10 | extern crate floating_duration; 11 | use std::time::Instant; 12 | use std::env; 13 | use std::fs::File; 14 | use std::io::{self, Read, Write, BufRead, BufReader}; 15 | use std::io::prelude::*; 16 | 17 | #[derive(Clone)] 18 | struct Trip { 19 | dst: u64, 20 | up: f64, 21 | down: f64 22 | } 23 | 24 | fn main() 25 | { 26 | let simlog = File::open("simulation.log").expect("read simulation log"); 27 | let mut simlog = BufReader::new(&simlog); 28 | let mut esp = None; 29 | let mut jerk = 0.0; 30 | let mut prev_est: Option = None; 31 | let mut dst_timing: Vec = Vec::new(); 32 | let mut start_location = 0.0; 33 | for line in simlog.lines() { 34 | let l = line.unwrap(); 35 | match esp { 36 | None => { 37 | let spec: (u64,f64,f64,u64) = serde_json::from_str(&l).unwrap(); 38 | esp = Some(ElevatorSpecification::load(spec)); 39 | }, 40 | Some(ref esp) => { 41 | let (est, dst): ((f64,f64,f64,f64,f64),u64) = serde_json::from_str(&l).unwrap(); 42 | let est = ElevatorState::load(est); 43 | let dl = dst_timing.len(); 44 | if dst_timing.len()==0 || dst_timing[dl-1].dst != dst { 45 | dst_timing.push(Trip { dst:dst, up:0.0, down:0.0 }); 46 | } 47 | 48 | if let Some(prev_est) = prev_est { 49 | let dt = est.timestamp - prev_est.timestamp; 50 | if est.velocity > 0.0 { 51 | dst_timing[dl-1].up += dt; 52 | } else { 53 | dst_timing[dl-1].down += dt; 54 | } 55 | let da = (est.acceleration - prev_est.acceleration).abs(); 56 | jerk = (jerk * (1.0 - dt)) + (da * dt); 57 | if jerk.abs() > 0.22 { 58 | panic!("jerk is outside of acceptable limits: {} {:?}", jerk, est.dump()) 59 | } 60 | } else { 61 | start_location = est.location; 62 | } 63 | if est.acceleration.abs() > 2.2 { 64 | panic!("acceleration is outside of acceptable limits: {:?}", est.dump()) 65 | } 66 | if est.velocity.abs() > 5.5 { 67 | panic!("velocity is outside of acceptable limits: {:?}", est.dump()) 68 | } 69 | prev_est = Some(est); 70 | } 71 | } 72 | } 73 | 74 | //elevator should not backup 75 | let mut total_time = 0.0; 76 | let mut total_direct = 0.0; 77 | for trip in dst_timing.clone() 78 | { 79 | total_time += (trip.up + trip.down); 80 | if trip.up > trip.down { 81 | total_direct += trip.up; 82 | } else { 83 | total_direct += trip.down; 84 | } 85 | } 86 | if (total_direct / total_time) < 0.9 { 87 | panic!("elevator back up is too common: {}", total_direct / total_time) 88 | } 89 | 90 | 91 | //trips should finish within 20% of theoretical limit 92 | let MAX_JERK = 0.2; 93 | let MAX_ACCELERATION = 2.0; 94 | let MAX_VELOCITY = 5.0; 95 | 96 | let mut trip_start_location = start_location; 97 | let mut theoretical_time = 0.0; 98 | let floor_height = esp.unwrap().floor_height; 99 | for trip in dst_timing.clone() 100 | { 101 | let next_floor = (trip.dst as f64) * floor_height; 102 | let d = (trip_start_location - next_floor).abs(); 103 | theoretical_time += ( 104 | 2.0*(MAX_ACCELERATION / MAX_JERK) + 105 | 2.0*(MAX_JERK / MAX_ACCELERATION) + 106 | d / MAX_VELOCITY 107 | ); 108 | trip_start_location = next_floor; 109 | } 110 | if total_time > (theoretical_time * 1.2) { 111 | panic!("elevator moves to slow {} {}", total_time, theoretical_time * 1.2) 112 | } 113 | 114 | println!("All simulation checks passing."); 115 | } 116 | -------------------------------------------------------------------------------- /Chapter04/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate elevator; 2 | 3 | fn main() 4 | { 5 | elevator::run_simulation(); 6 | } 7 | -------------------------------------------------------------------------------- /Chapter04/src/motor.rs: -------------------------------------------------------------------------------- 1 | extern crate floating_duration; 2 | use std::time::Instant; 3 | use floating_duration::{TimeAsFloat, TimeFormat}; 4 | use physics::{ElevatorSpecification, ElevatorState, MotorInput, SimpleMotorInput, MotorController}; 5 | 6 | pub struct SimpleMotorController 7 | { 8 | pub esp: ElevatorSpecification 9 | } 10 | 11 | impl MotorController for SimpleMotorController 12 | { 13 | fn init(&mut self, esp: ElevatorSpecification, est: ElevatorState) 14 | { 15 | self.esp = esp; 16 | } 17 | 18 | fn poll(&mut self, est: ElevatorState, dst: u64) -> Box 19 | { 20 | //5.3. Adjust motor control to process next floor request 21 | 22 | //it will take t seconds to decelerate from velocity v at -1 m/s^2 23 | let t = est.velocity.abs() / 1.0; 24 | 25 | //during which time, the carriage will travel d=t * v/2 meters 26 | //at an average velocity of v/2 before stopping 27 | let d = t * (est.velocity/2.0); 28 | 29 | //l = distance to next floor 30 | let l = (est.location - (dst as f64)*self.esp.floor_height).abs(); 31 | 32 | let target_acceleration = { 33 | //are we going up? 34 | let going_up = est.location < (dst as f64)*self.esp.floor_height; 35 | 36 | //Do not exceed maximum velocity 37 | if est.velocity.abs() >= 5.0 { 38 | if going_up==(est.velocity>0.0) { 39 | 0.0 40 | //decelerate if going in wrong direction 41 | } else if going_up { 42 | 1.0 43 | } else { 44 | -1.0 45 | } 46 | 47 | //if within comfortable deceleration range and moving in right direction, decelerate 48 | } else if l < d && going_up==(est.velocity>0.0) { 49 | if going_up { 50 | -1.0 51 | } else { 52 | 1.0 53 | } 54 | 55 | //else if not at peak velocity, accelerate 56 | } else { 57 | if going_up { 58 | 1.0 59 | } else { 60 | -1.0 61 | } 62 | } 63 | }; 64 | 65 | let gravity_adjusted_acceleration = target_acceleration + 9.8; 66 | let target_force = gravity_adjusted_acceleration * self.esp.carriage_weight; 67 | let target_voltage = self.esp.motor.voltage_of_force(target_force); 68 | if target_voltage > 0.0 { 69 | Box::new(SimpleMotorInput::Up { voltage: target_voltage }) 70 | } else { 71 | Box::new(SimpleMotorInput::Down { voltage: target_voltage.abs() }) 72 | } 73 | } 74 | } 75 | 76 | pub struct SmoothMotorController 77 | { 78 | pub esp: ElevatorSpecification, 79 | pub timestamp: f64 80 | } 81 | 82 | impl MotorController for SmoothMotorController 83 | { 84 | fn init(&mut self, esp: ElevatorSpecification, est: ElevatorState) 85 | { 86 | self.esp = esp; 87 | self.timestamp = est.timestamp; 88 | } 89 | 90 | fn poll(&mut self, est: ElevatorState, dst: u64) -> Box 91 | { 92 | //5.3. Adjust motor control to process next floor request 93 | 94 | let MAX_JERK = 0.2; 95 | let MAX_ACCELERATION = 2.0; 96 | let MAX_VELOCITY = 5.0; 97 | 98 | //it will take t seconds to reach max from max 99 | let t_accel = MAX_ACCELERATION / MAX_JERK; 100 | let t_veloc = MAX_VELOCITY / MAX_ACCELERATION; 101 | 102 | //it may take up to d meters to decelerate from current 103 | let decel_t = if (est.velocity>0.0) == (est.acceleration>0.0) { 104 | //this case deliberately overestimates d to prevent "back up" 105 | (est.acceleration.abs() / MAX_JERK) + 106 | (est.velocity.abs() / (MAX_ACCELERATION / 2.0)) + 107 | 2.0 * (MAX_ACCELERATION / MAX_JERK) 108 | } else { 109 | //without the MAX_JERK, this approaches infinity and decelerates way too soon 110 | //MAX_JERK * 1s = acceleration in m/s^2 111 | est.velocity.abs() / (MAX_JERK + est.acceleration.abs()) 112 | }; 113 | let d = est.velocity.abs() * decel_t; 114 | 115 | //l = distance to next floor 116 | let l = (est.location - (dst as f64)*self.esp.floor_height).abs(); 117 | 118 | let target_acceleration = { 119 | //are we going up? 120 | let going_up = est.location < (dst as f64)*self.esp.floor_height; 121 | 122 | //time elapsed since last poll 123 | let dt = est.timestamp - self.timestamp; 124 | self.timestamp = est.timestamp; 125 | 126 | //Do not exceed maximum acceleration 127 | if est.acceleration.abs() >= MAX_ACCELERATION { 128 | if est.acceleration > 0.0 { 129 | est.acceleration - (dt * MAX_JERK) 130 | } else { 131 | est.acceleration + (dt * MAX_JERK) 132 | } 133 | 134 | //Do not exceed maximum velocity 135 | } else if est.velocity.abs() >= MAX_VELOCITY 136 | || (est.velocity + est.acceleration * (est.acceleration.abs() / MAX_JERK)).abs() >= MAX_VELOCITY { 137 | if est.velocity > 0.0 { 138 | est.acceleration - (dt * MAX_JERK) 139 | } else { 140 | est.acceleration + (dt * MAX_JERK) 141 | } 142 | 143 | //if within comfortable deceleration range and moving in right direction, decelerate 144 | } else if l < d && (est.velocity>0.0) == going_up { 145 | if going_up { 146 | est.acceleration - (dt * MAX_JERK) 147 | } else { 148 | est.acceleration + (dt * MAX_JERK) 149 | } 150 | 151 | //else if not at peak velocity, accelerate smoothly 152 | } else { 153 | if going_up { 154 | est.acceleration + (dt * MAX_JERK) 155 | } else { 156 | est.acceleration - (dt * MAX_JERK) 157 | } 158 | } 159 | }; 160 | 161 | let gravity_adjusted_acceleration = target_acceleration + 9.8; 162 | let target_force = gravity_adjusted_acceleration * self.esp.carriage_weight; 163 | let target_voltage = self.esp.motor.voltage_of_force(target_force); 164 | if !target_voltage.is_finite() { 165 | //divide by zero etc. 166 | //may happen if time delta underflows 167 | Box::new(SimpleMotorInput::Up { voltage: 0.0 }) 168 | } else if target_voltage > 0.0 { 169 | Box::new(SimpleMotorInput::Up { voltage: target_voltage }) 170 | } else { 171 | Box::new(SimpleMotorInput::Down { voltage: target_voltage.abs() }) 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Chapter04/src/physics.rs: -------------------------------------------------------------------------------- 1 | extern crate floating_duration; 2 | use std::time::Instant; 3 | use floating_duration::{TimeAsFloat, TimeFormat}; 4 | use std::{thread, time}; 5 | use serde; 6 | 7 | pub trait Motor 8 | { 9 | fn force_of_voltage(&self, v: f64) -> f64; 10 | fn voltage_of_force(&self, v: f64) -> f64; 11 | } 12 | 13 | pub struct SimpleMotor; 14 | impl Motor for SimpleMotor 15 | { 16 | fn force_of_voltage(&self, v: f64) -> f64 17 | { 18 | 8.0 * v 19 | } 20 | fn voltage_of_force(&self, v: f64) -> f64 21 | { 22 | v / 8.0 23 | } 24 | } 25 | 26 | #[derive(Clone,Serialize,Deserialize,Debug)] 27 | pub enum SimpleMotorInput 28 | { 29 | Up { voltage: f64 }, 30 | Down { voltage: f64 } 31 | } 32 | 33 | pub trait MotorInput: MotorForce + MotorVoltage 34 | { 35 | } 36 | impl MotorInput for SimpleMotorInput {} 37 | 38 | pub struct ElevatorSpecification 39 | { 40 | pub floor_count: u64, 41 | pub floor_height: f64, 42 | pub carriage_weight: f64, 43 | pub motor: Box 44 | } 45 | pub trait ElevatorSpecificationClone 46 | { 47 | fn clone(&self) -> ElevatorSpecification; 48 | fn dump(&self) -> (u64,f64,f64,u64); 49 | fn load((u64,f64,f64,u64)) -> ElevatorSpecification; 50 | } 51 | impl ElevatorSpecificationClone for ElevatorSpecification 52 | { 53 | fn clone(&self) -> ElevatorSpecification 54 | { 55 | ElevatorSpecification 56 | { 57 | floor_count: self.floor_count, 58 | floor_height: self.floor_height, 59 | carriage_weight: self.carriage_weight, 60 | motor: Box::new(SimpleMotor) 61 | } 62 | } 63 | fn dump(&self) -> (u64,f64,f64,u64) 64 | { 65 | (self.floor_count, 66 | self.floor_height, 67 | self.carriage_weight, 68 | 0) 69 | } 70 | fn load(esp: (u64,f64,f64,u64)) -> ElevatorSpecification 71 | { 72 | ElevatorSpecification 73 | { 74 | floor_count: esp.0, 75 | floor_height: esp.1, 76 | carriage_weight: esp.2, 77 | motor: Box::new(SimpleMotor) 78 | } 79 | } 80 | } 81 | 82 | pub struct ElevatorState 83 | { 84 | pub timestamp: f64, 85 | pub location: f64, 86 | pub velocity: f64, 87 | pub acceleration: f64, 88 | pub motor_input: Box 89 | } 90 | pub trait ElevatorStateClone 91 | { 92 | fn clone(&self) -> ElevatorState; 93 | fn dump(&self) -> (f64,f64,f64,f64,f64); 94 | fn load((f64,f64,f64,f64,f64)) -> ElevatorState; 95 | } 96 | impl ElevatorStateClone for ElevatorState 97 | { 98 | fn clone(&self) -> ElevatorState 99 | { 100 | ElevatorState 101 | { 102 | timestamp: self.timestamp, 103 | location: self.location, 104 | velocity: self.velocity, 105 | acceleration: self.acceleration, 106 | motor_input: simple_from_voltage(self.motor_input.voltage()) 107 | } 108 | } 109 | fn dump(&self) -> (f64,f64,f64,f64,f64) 110 | { 111 | (self.timestamp, 112 | self.location, 113 | self.velocity, 114 | self.acceleration, 115 | self.motor_input.voltage()) 116 | } 117 | fn load(est: (f64,f64,f64,f64,f64)) -> ElevatorState 118 | { 119 | ElevatorState 120 | { 121 | timestamp: est.0, 122 | location: est.1, 123 | velocity: est.2, 124 | acceleration: est.3, 125 | motor_input: simple_from_voltage(est.4) 126 | } 127 | } 128 | } 129 | 130 | pub type FloorRequests = Vec; 131 | 132 | pub trait MotorController 133 | { 134 | fn init(&mut self, esp: ElevatorSpecification, est: ElevatorState); 135 | fn poll(&mut self, est: ElevatorState, dst: u64) -> Box; 136 | } 137 | 138 | pub trait DataRecorder 139 | { 140 | fn init(&mut self, esp: ElevatorSpecification, est: ElevatorState); 141 | fn poll(&mut self, est: ElevatorState, dst: u64); 142 | } 143 | 144 | pub trait MotorForce { 145 | fn calculate_force(&self) -> f64; 146 | } 147 | impl MotorForce for SimpleMotorInput { 148 | fn calculate_force(&self) -> f64 149 | { 150 | match *self { 151 | SimpleMotorInput::Up { voltage: v } => { v * 8.0 } 152 | SimpleMotorInput::Down { voltage: v } => { v * -8.0 } 153 | } 154 | } 155 | } 156 | 157 | fn simple_from_voltage(v: f64) -> Box 158 | { 159 | if v>0.0 { 160 | Box::new(SimpleMotorInput::Up { voltage: v }) 161 | } else { 162 | Box::new(SimpleMotorInput::Down { voltage: v.abs() }) 163 | } 164 | } 165 | 166 | pub trait MotorVoltage { 167 | fn voltage(&self) -> f64; 168 | } 169 | impl MotorVoltage for SimpleMotorInput { 170 | fn voltage(&self) -> f64 171 | { 172 | match *self { 173 | SimpleMotorInput::Up { voltage: v } => { v } 174 | SimpleMotorInput::Down { voltage: v } => { -v } 175 | } 176 | } 177 | } 178 | 179 | pub fn simulate_elevator(esp: ElevatorSpecification, est: ElevatorState, req: FloorRequests, 180 | mc: &mut MC, dr: &mut DR) { 181 | 182 | //immutable input becomes mutable local state 183 | let mut esp = esp.clone(); 184 | let mut est = est.clone(); 185 | let mut req = req.clone(); 186 | 187 | //initialize MotorController and DataController 188 | mc.init(esp.clone(), est.clone()); 189 | dr.init(esp.clone(), est.clone()); 190 | 191 | //5. Loop while there are remaining floor requests 192 | let original_ts = Instant::now(); 193 | thread::sleep(time::Duration::from_millis(1)); 194 | while req.len() > 0 195 | { 196 | //5.1. Update location, velocity, and acceleration 197 | let now = Instant::now(); 198 | let ts = now.duration_since(original_ts) 199 | .as_fractional_secs(); 200 | let dt = ts - est.timestamp; 201 | est.timestamp = ts; 202 | 203 | est.location = est.location + est.velocity * dt; 204 | est.velocity = est.velocity + est.acceleration * dt; 205 | est.acceleration = { 206 | let F = est.motor_input.calculate_force(); 207 | let m = esp.carriage_weight; 208 | -9.8 + F/m 209 | }; 210 | 211 | //5.2. If next floor request in queue is satisfied, then remove from queue 212 | let next_floor = req[0]; 213 | if (est.location - (next_floor as f64)*esp.floor_height).abs() < 0.01 && 214 | est.velocity.abs() < 0.01 215 | { 216 | est.velocity = 0.0; 217 | req.remove(0); 218 | } 219 | 220 | //5.4. Print realtime statistics 221 | dr.poll(est.clone(), next_floor); 222 | 223 | //5.3. Adjust motor control to process next floor request 224 | est.motor_input = mc.poll(est.clone(), next_floor); 225 | 226 | thread::sleep(time::Duration::from_millis(1)); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /Chapter05/.gitignore: -------------------------------------------------------------------------------- 1 | src/*.o 2 | -------------------------------------------------------------------------------- /Chapter05/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "elevator" 3 | version = "1.0.0" 4 | build = "build.rs" 5 | 6 | [dependencies] 7 | floating-duration = "0.1.2" 8 | termion = "1.0" 9 | timebomb = "0.1" 10 | serde = "1.0" 11 | serde_json = "1.0" 12 | serde_derive = "1.0" 13 | libc = "0.2" 14 | 15 | [[bin]] 16 | name = "simulate_trip" 17 | path = "src/simulate_trip.rs" 18 | 19 | [[bin]] 20 | name = "analyze_trip" 21 | path = "src/analyze_trip.rs" 22 | 23 | [[bin]] 24 | name = "operate_elevator" 25 | path = "src/operate_elevator.rs" 26 | -------------------------------------------------------------------------------- /Chapter05/README.md: -------------------------------------------------------------------------------- 1 | Linked object files are platform dependent. The C files and headers are provided, and a build/link script is provided. 2 | The build script is somewhat platform dependent and requires GCC to be installed. It may be necessary to adjust build.rs 3 | to compile this Chapter's code. 4 | -------------------------------------------------------------------------------- /Chapter05/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | use std::env; 3 | use std::path::Path; 4 | 5 | fn main() { 6 | let out_dir = env::var("OUT_DIR").unwrap(); 7 | 8 | Command::new("gcc").args(&["src/motor1.c", "-c", "-fPIC", "-o"]) 9 | .arg(&format!("{}/motor1.o", out_dir)) 10 | .status().unwrap(); 11 | Command::new("ar").args(&["crus", "libmotor1.a", "motor1.o"]) 12 | .current_dir(&Path::new(&out_dir)) 13 | .status().unwrap(); 14 | Command::new("gcc").args(&["src/motor2.c", "-c", "-fPIC", "-o"]) 15 | .arg(&format!("{}/motor2.o", out_dir)) 16 | .status().unwrap(); 17 | Command::new("ar").args(&["crus", "libmotor2.a", "motor2.o"]) 18 | .current_dir(&Path::new(&out_dir)) 19 | .status().unwrap(); 20 | Command::new("gcc").args(&["src/motor3.c", "-c", "-fPIC", "-o"]) 21 | .arg(&format!("{}/motor3.o", out_dir)) 22 | .status().unwrap(); 23 | Command::new("ar").args(&["crus", "libmotor3.a", "motor3.o"]) 24 | .current_dir(&Path::new(&out_dir)) 25 | .status().unwrap(); 26 | 27 | Command::new("gcc").args(&["src/elevator1.c", "-c", "-fPIC", "-o"]) 28 | .arg(&format!("{}/elevator1.o", out_dir)) 29 | .status().unwrap(); 30 | Command::new("ar").args(&["crus", "libelevator1.a", "elevator1.o"]) 31 | .current_dir(&Path::new(&out_dir)) 32 | .status().unwrap(); 33 | Command::new("gcc").args(&["src/elevator2.c", "-c", "-fPIC", "-o"]) 34 | .arg(&format!("{}/elevator2.o", out_dir)) 35 | .status().unwrap(); 36 | Command::new("ar").args(&["crus", "libelevator2.a", "elevator2.o"]) 37 | .current_dir(&Path::new(&out_dir)) 38 | .status().unwrap(); 39 | Command::new("gcc").args(&["src/elevator3.c", "-c", "-fPIC", "-o"]) 40 | .arg(&format!("{}/elevator3.o", out_dir)) 41 | .status().unwrap(); 42 | Command::new("ar").args(&["crus", "libelevator3.a", "elevator3.o"]) 43 | .current_dir(&Path::new(&out_dir)) 44 | .status().unwrap(); 45 | 46 | println!("cargo:rustc-link-search=native={}", out_dir); 47 | println!("cargo:rustc-link-lib=static=motor1"); 48 | println!("cargo:rustc-link-lib=static=motor2"); 49 | println!("cargo:rustc-link-lib=static=motor3"); 50 | println!("cargo:rustc-link-lib=static=elevator1"); 51 | println!("cargo:rustc-link-lib=static=elevator2"); 52 | println!("cargo:rustc-link-lib=static=elevator3"); 53 | } 54 | -------------------------------------------------------------------------------- /Chapter05/src/analyze_trip.rs: -------------------------------------------------------------------------------- 1 | extern crate elevator; 2 | 3 | #[macro_use] extern crate serde_derive; 4 | extern crate serde; 5 | extern crate serde_json; 6 | extern crate floating_duration; 7 | use std::time::Instant; 8 | use std::env; 9 | use std::fs::File; 10 | use std::io::{self, Read, Write, BufRead, BufReader}; 11 | use std::io::prelude::*; 12 | 13 | use elevator::buildings; 14 | use elevator::buildings::{Building, getCumulativeFloorHeight}; 15 | use elevator::physics::{ElevatorState}; 16 | 17 | #[derive(Clone)] 18 | struct Trip { 19 | dst: u64, 20 | up: f64, 21 | down: f64 22 | } 23 | 24 | fn main() 25 | { 26 | let simlog = File::open("simulation.log").expect("read simulation log"); 27 | let mut simlog = BufReader::new(&simlog); 28 | let mut jerk = 0.0; 29 | let mut prev_est: Option = None; 30 | let mut dst_timing: Vec = Vec::new(); 31 | let mut start_location = 0.0; 32 | 33 | let mut first_line = String::new(); 34 | let len = simlog.read_line(&mut first_line).unwrap(); 35 | let spec: u64 = serde_json::from_str(&first_line).unwrap(); 36 | let esp: Box = buildings::deserialize(spec); 37 | for line in simlog.lines() { 38 | let l = line.unwrap(); 39 | let (est, dst): (ElevatorState,u64) = serde_json::from_str(&l).unwrap(); 40 | let dl = dst_timing.len(); 41 | if dst_timing.len()==0 || dst_timing[dl-1].dst != dst { 42 | dst_timing.push(Trip { dst:dst, up:0.0, down:0.0 }); 43 | } 44 | 45 | if let Some(prev_est) = prev_est { 46 | let dt = est.timestamp - prev_est.timestamp; 47 | if est.velocity > 0.0 { 48 | dst_timing[dl-1].up += dt; 49 | } else { 50 | dst_timing[dl-1].down += dt; 51 | } 52 | let da = (est.acceleration - prev_est.acceleration).abs(); 53 | jerk = (jerk * (1.0 - dt)) + (da * dt); 54 | if jerk.abs() > 0.22 { 55 | panic!("jerk is outside of acceptable limits: {} {:?}", jerk, est) 56 | } 57 | } else { 58 | start_location = est.location; 59 | } 60 | if est.acceleration.abs() > 2.2 { 61 | panic!("acceleration is outside of acceptable limits: {:?}", est) 62 | } 63 | if est.velocity.abs() > 5.5 { 64 | panic!("velocity is outside of acceptable limits: {:?}", est) 65 | } 66 | prev_est = Some(est); 67 | } 68 | 69 | //elevator should not backup 70 | let mut total_time = 0.0; 71 | let mut total_direct = 0.0; 72 | for trip in dst_timing.clone() 73 | { 74 | total_time += (trip.up + trip.down); 75 | if trip.up > trip.down { 76 | total_direct += trip.up; 77 | } else { 78 | total_direct += trip.down; 79 | } 80 | } 81 | if (total_direct / total_time) < 0.9 { 82 | panic!("elevator back up is too common: {}", total_direct / total_time) 83 | } 84 | 85 | 86 | //trips should finish within 20% of theoretical limit 87 | let MAX_JERK = 0.2; 88 | let MAX_ACCELERATION = 2.0; 89 | let MAX_VELOCITY = 5.0; 90 | 91 | let mut trip_start_location = start_location; 92 | let mut theoretical_time = 0.0; 93 | let floor_heights = esp.get_floor_heights(); 94 | for trip in dst_timing.clone() 95 | { 96 | let next_floor = getCumulativeFloorHeight(floor_heights.clone(), trip.dst); 97 | let d = (trip_start_location - next_floor).abs(); 98 | theoretical_time += ( 99 | 2.0*(MAX_ACCELERATION / MAX_JERK) + 100 | 2.0*(MAX_JERK / MAX_ACCELERATION) + 101 | d / MAX_VELOCITY 102 | ); 103 | trip_start_location = next_floor; 104 | } 105 | if total_time > (theoretical_time * 1.2) { 106 | panic!("elevator moves to slow {} {}", total_time, theoretical_time * 1.2) 107 | } 108 | 109 | println!("All simulation checks passing."); 110 | } 111 | -------------------------------------------------------------------------------- /Chapter05/src/buildings.rs: -------------------------------------------------------------------------------- 1 | use elevator_drivers::{ElevatorDriver, ElevatorDriver1, ElevatorDriver2, ElevatorDriver3}; 2 | use motor_controllers::{MotorController, newMotorController1, newMotorController2, newMotorController3}; 3 | 4 | pub trait Building 5 | { 6 | fn get_elevator_driver(&self) -> Box; 7 | fn get_motor_controller(&self) -> Box; 8 | fn get_floor_heights(&self) -> Vec; 9 | fn get_carriage_weight(&self) -> f64; 10 | fn clone(&self) -> Box; 11 | fn serialize(&self) -> u64; 12 | } 13 | 14 | pub fn deserialize(n: u64) -> Box 15 | { 16 | if n==1 { 17 | Box::new(Building1) 18 | } else if n==2 { 19 | Box::new(Building2) 20 | } else { 21 | Box::new(Building3) 22 | } 23 | } 24 | 25 | pub fn getCarriageFloor(floorHeights: Vec, height: f64) -> u64 26 | { 27 | let mut c = 0.0; 28 | for (fi, fht) in floorHeights.iter().enumerate() { 29 | c += fht; 30 | if height <= c { 31 | return (fi as u64) 32 | } 33 | } 34 | (floorHeights.len()-1) as u64 35 | } 36 | 37 | pub fn getCumulativeFloorHeight(heights: Vec, floor: u64) -> f64 38 | { 39 | heights.iter().take(floor as usize).sum() 40 | } 41 | 42 | pub struct Building1; 43 | impl Building for Building1 { 44 | fn get_elevator_driver(&self) -> Box 45 | { 46 | Box::new(ElevatorDriver1) 47 | } 48 | fn get_motor_controller(&self) -> Box 49 | { 50 | newMotorController1() 51 | } 52 | fn get_floor_heights(&self) -> Vec 53 | { 54 | vec![8.0, 4.0, 4.0, 4.0, 4.0] 55 | } 56 | fn get_carriage_weight(&self) -> f64 57 | { 58 | 1200.0 59 | } 60 | fn clone(&self) -> Box { 61 | Box::new(Building1) 62 | } 63 | fn serialize(&self) -> u64 64 | { 65 | 1 66 | } 67 | } 68 | 69 | pub struct Building2; 70 | impl Building for Building2 { 71 | fn get_elevator_driver(&self) -> Box 72 | { 73 | Box::new(ElevatorDriver2) 74 | } 75 | fn get_motor_controller(&self) -> Box 76 | { 77 | newMotorController2() 78 | } 79 | fn get_floor_heights(&self) -> Vec 80 | { 81 | vec![5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0] 82 | } 83 | fn get_carriage_weight(&self) -> f64 84 | { 85 | 1350.0 86 | } 87 | fn clone(&self) -> Box { 88 | Box::new(Building2) 89 | } 90 | fn serialize(&self) -> u64 91 | { 92 | 2 93 | } 94 | } 95 | 96 | pub struct Building3; 97 | impl Building for Building3 { 98 | fn get_elevator_driver(&self) -> Box 99 | { 100 | Box::new(ElevatorDriver3) 101 | } 102 | fn get_motor_controller(&self) -> Box 103 | { 104 | newMotorController3() 105 | } 106 | fn get_floor_heights(&self) -> Vec 107 | { 108 | vec![6.0, 4.0, 4.0, 4.0] 109 | } 110 | fn get_carriage_weight(&self) -> f64 111 | { 112 | 1400.0 113 | } 114 | fn clone(&self) -> Box { 115 | Box::new(Building3) 116 | } 117 | fn serialize(&self) -> u64 118 | { 119 | 3 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Chapter05/src/data_recorders.rs: -------------------------------------------------------------------------------- 1 | use buildings::{Building, getCarriageFloor}; 2 | use physics::{ElevatorState}; 3 | use std::fs::File; 4 | use std::io::{self, Read, Write}; 5 | use std::io::prelude::*; 6 | use termion; 7 | use termion::{clear, cursor, style}; 8 | use termion::raw; 9 | use termion::raw::IntoRawMode; 10 | use termion::input::TermRead; 11 | use termion::event::Key; 12 | use serde_json; 13 | 14 | pub trait DataRecorder 15 | { 16 | fn init(&mut self, esp: Box, est: ElevatorState); 17 | fn record(&mut self, est: ElevatorState, dst: u64); 18 | fn summary(&mut self); 19 | } 20 | 21 | struct SimpleDataRecorder 22 | { 23 | esp: Box, 24 | termwidth: u64, 25 | termheight: u64, 26 | stdout: raw::RawTerminal, 27 | log: File, 28 | record_location: Vec, 29 | record_velocity: Vec, 30 | record_acceleration: Vec, 31 | record_force: Vec, 32 | } 33 | 34 | pub fn newSimpleDataRecorder(esp: Box) -> Box 35 | { 36 | let termsize = termion::terminal_size().ok(); 37 | Box::new(SimpleDataRecorder { 38 | esp: esp.clone(), 39 | termwidth: termsize.map(|(w,_)| w-2).expect("termwidth") as u64, 40 | termheight: termsize.map(|(_,h)| h-2).expect("termheight") as u64, 41 | stdout: io::stdout().into_raw_mode().unwrap(), 42 | log: File::create("simulation.log").expect("log file"), 43 | record_location: Vec::new(), 44 | record_velocity: Vec::new(), 45 | record_acceleration: Vec::new(), 46 | record_force: Vec::new() 47 | }) 48 | } 49 | 50 | impl DataRecorder for SimpleDataRecorder 51 | { 52 | fn init(&mut self, esp: Box, est: ElevatorState) 53 | { 54 | self.esp = esp.clone(); 55 | self.log.write_all(serde_json::to_string(&esp.serialize()).unwrap().as_bytes()).expect("write spec to log"); 56 | self.log.write_all(b"\r\n").expect("write spec to log"); 57 | } 58 | fn record(&mut self, est: ElevatorState, dst: u64) 59 | { 60 | let datum = serde_json::to_string(&(est.clone(), dst)).unwrap(); 61 | self.log.write_all(datum.as_bytes()).expect("write state to log"); 62 | self.log.write_all(b"\r\n").expect("write state to log"); 63 | 64 | self.record_location.push(est.location); 65 | self.record_velocity.push(est.velocity); 66 | self.record_acceleration.push(est.acceleration); 67 | self.record_force.push(est.motor_input); 68 | 69 | //5.4. Print realtime statistics 70 | print!("{}{}{}", clear::All, cursor::Goto(1, 1), cursor::Hide); 71 | let carriage_floor = getCarriageFloor(self.esp.get_floor_heights(), est.location); 72 | let floor_count = self.esp.get_floor_heights().len() as u64; 73 | let mut terminal_buffer = vec![' ' as u8; (self.termwidth*self.termheight) as usize]; 74 | for ty in 0..floor_count 75 | { 76 | terminal_buffer[ (ty*self.termwidth + 0) as usize ] = '[' as u8; 77 | terminal_buffer[ (ty*self.termwidth + 1) as usize ] = 78 | if (ty as u64)==((floor_count-1)-carriage_floor) { 'X' as u8 } 79 | else { ' ' as u8 }; 80 | terminal_buffer[ (ty*self.termwidth + 2) as usize ] = ']' as u8; 81 | terminal_buffer[ (ty*self.termwidth + self.termwidth-2) as usize ] = '\r' as u8; 82 | terminal_buffer[ (ty*self.termwidth + self.termwidth-1) as usize ] = '\n' as u8; 83 | } 84 | let stats = vec![ 85 | format!("Carriage at floor {}", carriage_floor+1), 86 | format!("Location {:.06}", est.location), 87 | format!("Velocity {:.06}", est.velocity), 88 | format!("Acceleration {:.06}", est.acceleration), 89 | format!("Force [up-down] {:.06}", est.motor_input), 90 | ]; 91 | for sy in 0..stats.len() 92 | { 93 | for (sx,sc) in stats[sy].chars().enumerate() 94 | { 95 | terminal_buffer[ sy*(self.termwidth as usize) + 6 + sx ] = sc as u8; 96 | } 97 | } 98 | write!(self.stdout, "{}", String::from_utf8(terminal_buffer).ok().unwrap()); 99 | self.stdout.flush().unwrap(); 100 | } 101 | fn summary(&mut self) 102 | { 103 | //6 Calculate and print summary statistics 104 | write!(self.stdout, "{}{}{}", clear::All, cursor::Goto(1, 1), cursor::Show).unwrap(); 105 | variable_summary(&mut self.stdout, "location".to_string(), &self.record_location); 106 | variable_summary(&mut self.stdout, "velocity".to_string(), &self.record_velocity); 107 | variable_summary(&mut self.stdout, "acceleration".to_string(), &self.record_acceleration); 108 | variable_summary(&mut self.stdout, "force".to_string(), &self.record_force); 109 | self.stdout.flush().unwrap(); 110 | } 111 | } 112 | 113 | fn variable_summary(stdout: &mut raw::RawTerminal, vname: String, data: &Vec) { 114 | let (avg, dev) = variable_summary_stats(data); 115 | variable_summary_print(stdout, vname, avg, dev); 116 | } 117 | 118 | fn variable_summary_stats(data: &Vec) -> (f64, f64) 119 | { 120 | //calculate statistics 121 | let N = data.len(); 122 | let sum = data.iter().sum::(); 123 | let avg = sum / (N as f64); 124 | let dev = ( 125 | data.clone().into_iter() 126 | .map(|v| (v - avg).powi(2)) 127 | .sum::() 128 | / (N as f64) 129 | ).sqrt(); 130 | (avg, dev) 131 | } 132 | 133 | fn variable_summary_print(stdout: &mut raw::RawTerminal, vname: String, avg: f64, dev: f64) 134 | { 135 | //print formatted output 136 | writeln!(stdout, "Average of {:25}{:.6}", vname, avg); 137 | writeln!(stdout, "Standard deviation of {:14}{:.6}", vname, dev); 138 | writeln!(stdout, ""); 139 | } 140 | -------------------------------------------------------------------------------- /Chapter05/src/elevator1.c: -------------------------------------------------------------------------------- 1 | 2 | int elevator1_poll_floor_request() 3 | { 4 | //real implementation would interface with hardware here 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /Chapter05/src/elevator2.c: -------------------------------------------------------------------------------- 1 | 2 | int elevator2_poll_floor_request() 3 | { 4 | //real implementation would interface with hardware here 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /Chapter05/src/elevator3.c: -------------------------------------------------------------------------------- 1 | 2 | int elevator3_poll_floor_request() 3 | { 4 | //real implementation would interface with hardware here 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /Chapter05/src/elevator_drivers.rs: -------------------------------------------------------------------------------- 1 | use libc::c_int; 2 | 3 | #[link(name = "elevator1")] 4 | extern { 5 | pub fn elevator1_poll_floor_request() -> c_int; 6 | } 7 | 8 | #[link(name = "elevator2")] 9 | extern { 10 | pub fn elevator2_poll_floor_request() -> c_int; 11 | } 12 | 13 | #[link(name = "elevator3")] 14 | extern { 15 | pub fn elevator3_poll_floor_request() -> c_int; 16 | } 17 | 18 | pub trait ElevatorDriver 19 | { 20 | fn poll_floor_request(&self) -> Option; 21 | } 22 | 23 | pub struct ElevatorDriver1; 24 | impl ElevatorDriver for ElevatorDriver1 25 | { 26 | fn poll_floor_request(&self) -> Option 27 | { 28 | unsafe { 29 | let req = elevator1_poll_floor_request(); 30 | if req > 0 { 31 | Some(req as u64) 32 | } else { 33 | None 34 | } 35 | } 36 | } 37 | } 38 | 39 | pub struct ElevatorDriver2; 40 | impl ElevatorDriver for ElevatorDriver2 41 | { 42 | fn poll_floor_request(&self) -> Option 43 | { 44 | unsafe { 45 | let req = elevator2_poll_floor_request(); 46 | if req > 0 { 47 | Some(req as u64) 48 | } else { 49 | None 50 | } 51 | } 52 | } 53 | } 54 | 55 | pub struct ElevatorDriver3; 56 | impl ElevatorDriver for ElevatorDriver3 57 | { 58 | fn poll_floor_request(&self) -> Option 59 | { 60 | unsafe { 61 | let req = elevator3_poll_floor_request(); 62 | if req > 0 { 63 | Some(req as u64) 64 | } else { 65 | None 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Chapter05/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | #[macro_use] extern crate serde_derive; 3 | extern crate serde; 4 | extern crate serde_json; 5 | extern crate termion; 6 | extern crate floating_duration; 7 | 8 | pub mod motor_controllers; 9 | pub mod elevator_drivers; 10 | pub mod buildings; 11 | pub mod physics; 12 | pub mod trip_planning; 13 | pub mod data_recorders; 14 | pub mod motion_controllers; 15 | -------------------------------------------------------------------------------- /Chapter05/src/motion_controllers.rs: -------------------------------------------------------------------------------- 1 | use physics::{ElevatorState, MAX_JERK, MAX_ACCELERATION, MAX_VELOCITY}; 2 | use buildings::{Building, getCumulativeFloorHeight}; 3 | 4 | pub trait MotionController 5 | { 6 | fn init(&mut self, esp: Box, est: ElevatorState); 7 | fn adjust(&mut self, est: &ElevatorState, dst: u64) -> f64; 8 | } 9 | 10 | pub struct SmoothMotionController 11 | { 12 | pub esp: Box, 13 | pub timestamp: f64 14 | } 15 | 16 | impl MotionController for SmoothMotionController 17 | { 18 | fn init(&mut self, esp: Box, est: ElevatorState) 19 | { 20 | self.esp = esp; 21 | self.timestamp = est.timestamp; 22 | } 23 | 24 | fn adjust(&mut self, est: &ElevatorState, dst: u64) -> f64 25 | { 26 | //5.3. Adjust motor control to process next floor request 27 | 28 | //it will take t seconds to reach max from max 29 | let t_accel = MAX_ACCELERATION / MAX_JERK; 30 | let t_veloc = MAX_VELOCITY / MAX_ACCELERATION; 31 | 32 | //it may take up to d meters to decelerate from current 33 | let decel_t = if (est.velocity>0.0) == (est.acceleration>0.0) { 34 | //this case deliberately overestimates d to prevent "back up" 35 | (est.acceleration.abs() / MAX_JERK) + 36 | (est.velocity.abs() / (MAX_ACCELERATION / 2.0)) + 37 | 2.0 * (MAX_ACCELERATION / MAX_JERK) 38 | } else { 39 | //without the MAX_JERK, this approaches infinity and decelerates way too soon 40 | //MAX_JERK * 1s = acceleration in m/s^2 41 | est.velocity.abs() / (MAX_JERK + est.acceleration.abs()) 42 | }; 43 | let d = est.velocity.abs() * decel_t; 44 | 45 | let dst_height = getCumulativeFloorHeight(self.esp.get_floor_heights(), dst); 46 | 47 | //l = distance to next floor 48 | let l = (est.location - dst_height).abs(); 49 | 50 | 51 | let target_acceleration = { 52 | //are we going up? 53 | let going_up = est.location < dst_height; 54 | 55 | //time elapsed since last poll 56 | let dt = est.timestamp - self.timestamp; 57 | self.timestamp = est.timestamp; 58 | 59 | //Do not exceed maximum acceleration 60 | if est.acceleration.abs() >= MAX_ACCELERATION { 61 | if est.acceleration > 0.0 { 62 | est.acceleration - (dt * MAX_JERK) 63 | } else { 64 | est.acceleration + (dt * MAX_JERK) 65 | } 66 | 67 | //Do not exceed maximum velocity 68 | } else if est.velocity.abs() >= MAX_VELOCITY 69 | || (est.velocity + est.acceleration * (est.acceleration.abs() / MAX_JERK)).abs() >= MAX_VELOCITY { 70 | if est.velocity > 0.0 { 71 | est.acceleration - (dt * MAX_JERK) 72 | } else { 73 | est.acceleration + (dt * MAX_JERK) 74 | } 75 | 76 | //if within comfortable deceleration range and moving in right direction, decelerate 77 | } else if l < d && (est.velocity>0.0) == going_up { 78 | if going_up { 79 | est.acceleration - (dt * MAX_JERK) 80 | } else { 81 | est.acceleration + (dt * MAX_JERK) 82 | } 83 | 84 | //else if not at peak velocity, accelerate smoothly 85 | } else { 86 | if going_up { 87 | est.acceleration + (dt * MAX_JERK) 88 | } else { 89 | est.acceleration - (dt * MAX_JERK) 90 | } 91 | } 92 | }; 93 | 94 | let gravity_adjusted_acceleration = target_acceleration + 9.8; 95 | let target_force = gravity_adjusted_acceleration * self.esp.get_carriage_weight(); 96 | if !target_force.is_finite() { 97 | //divide by zero etc. 98 | //may happen if time delta underflows 99 | 0.0 100 | } else { 101 | target_force 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Chapter05/src/motor1.c: -------------------------------------------------------------------------------- 1 | 2 | double motor1_adjust_motor(double target_force){ 3 | //real driver would do something here 4 | //interface with physical components 5 | return target_force; 6 | } 7 | -------------------------------------------------------------------------------- /Chapter05/src/motor2.c: -------------------------------------------------------------------------------- 1 | 2 | double motor2_adjust_motor(double target_force){ 3 | //real driver would do something here 4 | //interface with physical components 5 | return target_force; 6 | } 7 | -------------------------------------------------------------------------------- /Chapter05/src/motor3.c: -------------------------------------------------------------------------------- 1 | 2 | double motor3_adjust_motor(double target_force){ 3 | //real driver would do something here 4 | //interface with physical components 5 | return target_force; 6 | } 7 | -------------------------------------------------------------------------------- /Chapter05/src/motor_controllers.rs: -------------------------------------------------------------------------------- 1 | use libc::c_double; 2 | 3 | use buildings::{Building}; 4 | use physics::{ElevatorState}; 5 | 6 | #[link(name = "motor1")] 7 | extern { 8 | pub fn motor1_adjust_motor(target_force: c_double) -> c_double; 9 | } 10 | 11 | #[link(name = "motor2")] 12 | extern { 13 | pub fn motor2_adjust_motor(target_force: c_double) -> c_double; 14 | } 15 | 16 | #[link(name = "motor3")] 17 | extern { 18 | pub fn motor3_adjust_motor(target_force: c_double) -> c_double; 19 | } 20 | 21 | #[derive(Clone,Serialize,Deserialize,Debug)] 22 | pub enum MotorInput 23 | { 24 | Motor1 { target_force: f64 }, 25 | Motor2 { target_force: f64 }, 26 | Motor3 { target_force: f64 }, 27 | } 28 | 29 | pub trait MotorDriver 30 | { 31 | fn adjust_motor(&self, input: MotorInput); 32 | } 33 | 34 | struct Motor1; 35 | impl MotorDriver for Motor1 36 | { 37 | fn adjust_motor(&self, input: MotorInput) 38 | { 39 | if let MotorInput::Motor1 { target_force: target_force } = input { 40 | unsafe { 41 | motor1_adjust_motor(target_force); 42 | } 43 | } 44 | } 45 | } 46 | 47 | struct Motor2; 48 | impl MotorDriver for Motor2 49 | { 50 | fn adjust_motor(&self, input: MotorInput) 51 | { 52 | if let MotorInput::Motor2 { target_force: target_force } = input { 53 | unsafe { 54 | motor2_adjust_motor(target_force); 55 | } 56 | } 57 | } 58 | } 59 | 60 | struct Motor3; 61 | impl MotorDriver for Motor3 62 | { 63 | fn adjust_motor(&self, input: MotorInput) 64 | { 65 | if let MotorInput::Motor3 { target_force: target_force } = input { 66 | unsafe { 67 | motor3_adjust_motor(target_force); 68 | } 69 | } 70 | } 71 | } 72 | 73 | pub trait MotorController 74 | { 75 | fn adjust_motor(&self, f: f64); 76 | fn max_force(&self) -> f64; 77 | } 78 | 79 | pub struct MotorController1 80 | { 81 | motor: Motor1 82 | } 83 | pub fn newMotorController1() -> Box 84 | { 85 | Box::new(MotorController1 { 86 | motor: Motor1 87 | }) 88 | } 89 | impl MotorController for MotorController1 90 | { 91 | fn adjust_motor(&self, f: f64) 92 | { 93 | self.motor.adjust_motor(MotorInput::Motor1 { 94 | target_force: f 95 | }) 96 | } 97 | fn max_force(&self) -> f64 98 | { 99 | 50000.0 100 | } 101 | } 102 | 103 | pub struct MotorController2 104 | { 105 | motor: Motor2 106 | } 107 | pub fn newMotorController2() -> Box 108 | { 109 | Box::new(MotorController2 { 110 | motor: Motor2 111 | }) 112 | } 113 | impl MotorController for MotorController2 114 | { 115 | fn adjust_motor(&self, f: f64) 116 | { 117 | self.motor.adjust_motor(MotorInput::Motor2 { 118 | target_force: f 119 | }) 120 | } 121 | fn max_force(&self) -> f64 122 | { 123 | 100000.0 124 | } 125 | } 126 | 127 | pub struct MotorController3 128 | { 129 | motor: Motor3 130 | } 131 | pub fn newMotorController3() -> Box 132 | { 133 | Box::new(MotorController3 { 134 | motor: Motor3 135 | }) 136 | } 137 | impl MotorController for MotorController3 138 | { 139 | fn adjust_motor(&self, f: f64) 140 | { 141 | self.motor.adjust_motor(MotorInput::Motor3 { 142 | target_force: f 143 | }) 144 | } 145 | fn max_force(&self) -> f64 146 | { 147 | 90000.0 148 | } 149 | } 150 | 151 | -------------------------------------------------------------------------------- /Chapter05/src/operate_elevator.rs: -------------------------------------------------------------------------------- 1 | extern crate elevator; 2 | extern crate floating_duration; 3 | 4 | use elevator::buildings::{Building, Building1, Building2, Building3, getCumulativeFloorHeight}; 5 | use elevator::trip_planning::{FloorRequests, RequestQueue}; 6 | use elevator::physics::{ElevatorState, simulate_elevator}; 7 | use elevator::motion_controllers::{SmoothMotionController, MotionController}; 8 | 9 | use std::collections::VecDeque; 10 | use floating_duration::{TimeAsFloat, TimeFormat}; 11 | use std::{thread, time}; 12 | use std::time::Instant; 13 | use std::env; 14 | use std::fs::File; 15 | use std::io::{self, Read, Write}; 16 | use std::io::prelude::*; 17 | use std::cmp; 18 | 19 | pub fn run_operator() 20 | { 21 | 22 | //1. Store location, velocity, and acceleration state 23 | //2. Store motor input target force 24 | let mut est = ElevatorState { 25 | timestamp: 0.0, 26 | location: 0.0, 27 | velocity: 0.0, 28 | acceleration: 0.0, 29 | motor_input: 0.0 30 | }; 31 | 32 | //3. Store input building description and floor requests 33 | let mut esp: Box = Box::new(Building1); 34 | let mut floor_requests: Box = Box::new(FloorRequests { 35 | requests: VecDeque::new() 36 | }); 37 | 38 | //4. Parse input and store as building description and static floor requests 39 | match env::args().nth(1) { 40 | Some(ref fp) if *fp == "-".to_string() => { 41 | let mut buffer = String::new(); 42 | io::stdin().read_to_string(&mut buffer) 43 | .expect("read_to_string failed"); 44 | 45 | for (li,l) in buffer.lines().enumerate() { 46 | if li==0 { 47 | let building = l.parse::().unwrap(); 48 | if building==0 { 49 | esp = Box::new(Building1); 50 | } else if building==1 { 51 | esp = Box::new(Building2); 52 | } else if building==2 { 53 | esp = Box::new(Building3); 54 | } else { 55 | panic!("unknown building code: {}", building); 56 | } 57 | } else { 58 | floor_requests.add_request(l.parse::().unwrap()); 59 | } 60 | } 61 | }, 62 | None => { 63 | let fp = "test1.txt"; 64 | let mut buffer = String::new(); 65 | File::open(fp) 66 | .expect("File::open failed") 67 | .read_to_string(&mut buffer) 68 | .expect("read_to_string failed"); 69 | 70 | for (li,l) in buffer.lines().enumerate() { 71 | if li==0 { 72 | let building = l.parse::().unwrap(); 73 | if building==0 { 74 | esp = Box::new(Building1); 75 | } else if building==1 { 76 | esp = Box::new(Building2); 77 | } else if building==2 { 78 | esp = Box::new(Building3); 79 | } else { 80 | panic!("unknown building code: {}", building); 81 | } 82 | } else { 83 | floor_requests.add_request(l.parse::().unwrap()); 84 | } 85 | } 86 | }, 87 | Some(fp) => { 88 | let mut buffer = String::new(); 89 | File::open(fp) 90 | .expect("File::open failed") 91 | .read_to_string(&mut buffer) 92 | .expect("read_to_string failed"); 93 | 94 | for (li,l) in buffer.lines().enumerate() { 95 | if li==0 { 96 | let building = l.parse::().unwrap(); 97 | if building==0 { 98 | esp = Box::new(Building1); 99 | } else if building==1 { 100 | esp = Box::new(Building2); 101 | } else if building==2 { 102 | esp = Box::new(Building3); 103 | } else { 104 | panic!("unknown building code: {}", building); 105 | } 106 | } else { 107 | floor_requests.add_request(l.parse::().unwrap()); 108 | } 109 | } 110 | } 111 | } 112 | 113 | let mut mc: Box = Box::new(SmoothMotionController { 114 | timestamp: 0.0, 115 | esp: esp.clone() 116 | }); 117 | 118 | //initialize MotorController and DataController 119 | mc.init(esp.clone(), est.clone()); 120 | 121 | //5. Loop while there are remaining floor requests 122 | let original_ts = Instant::now(); 123 | thread::sleep(time::Duration::from_millis(1)); 124 | let mut next_floor = floor_requests.pop_request(); 125 | while true 126 | { 127 | if let Some(dst) = next_floor { 128 | //5.1. Update location, velocity, and acceleration 129 | let now = Instant::now(); 130 | let ts = now.duration_since(original_ts) 131 | .as_fractional_secs(); 132 | let dt = ts - est.timestamp; 133 | est.timestamp = ts; 134 | 135 | est.location = est.location + est.velocity * dt; 136 | est.velocity = est.velocity + est.acceleration * dt; 137 | est.acceleration = { 138 | let F = est.motor_input; 139 | let m = esp.get_carriage_weight(); 140 | -9.8 + F/m 141 | }; 142 | 143 | //5.2. If next floor request in queue is satisfied, then remove from queue 144 | if (est.location - getCumulativeFloorHeight(esp.get_floor_heights(), dst)).abs() < 0.01 && 145 | est.velocity.abs() < 0.01 146 | { 147 | est.velocity = 0.0; 148 | next_floor = floor_requests.pop_request(); 149 | } 150 | 151 | //5.3. Adjust motor control to process next floor request 152 | est.motor_input = mc.adjust(&est, dst); 153 | 154 | //Adjust motor 155 | esp.get_motor_controller().adjust_motor(est.motor_input); 156 | 157 | thread::sleep(time::Duration::from_millis(1)); 158 | } else { 159 | //Adjust motor to not move 160 | esp.get_motor_controller().adjust_motor(0.0); 161 | } 162 | 163 | //check for dynamic floor requests 164 | if let Some(dst) = esp.get_elevator_driver().poll_floor_request() { 165 | floor_requests.add_request(dst); 166 | } 167 | } 168 | 169 | } 170 | 171 | fn main() 172 | { 173 | run_operator() 174 | } 175 | -------------------------------------------------------------------------------- /Chapter05/src/physics.rs: -------------------------------------------------------------------------------- 1 | use buildings::{Building,getCumulativeFloorHeight}; 2 | use trip_planning::{RequestQueue}; 3 | use motion_controllers::{MotionController}; 4 | use data_recorders::{DataRecorder}; 5 | use std::time::Instant; 6 | use floating_duration::{TimeAsFloat, TimeFormat}; 7 | use std::{thread, time}; 8 | 9 | #[derive(Clone,Debug,Serialize,Deserialize)] 10 | pub struct ElevatorState { 11 | pub timestamp: f64, 12 | pub location: f64, 13 | pub velocity: f64, 14 | pub acceleration: f64, 15 | pub motor_input: f64 16 | } 17 | 18 | pub const MAX_JERK: f64 = 20.0; 19 | pub const MAX_ACCELERATION: f64 = 2.0; 20 | pub const MAX_VELOCITY: f64 = 5.0; 21 | 22 | pub fn simulate_elevator(esp: Box, est: ElevatorState, floor_requests: &mut Box, 23 | mc: &mut Box, dr: &mut Box) 24 | { 25 | //immutable input becomes mutable local state 26 | let mut esp = esp.clone(); 27 | let mut est = est.clone(); 28 | 29 | //initialize MotorController and DataController 30 | mc.init(esp.clone(), est.clone()); 31 | dr.init(esp.clone(), est.clone()); 32 | 33 | //5. Loop while there are remaining floor requests 34 | let original_ts = Instant::now(); 35 | thread::sleep(time::Duration::from_millis(1)); 36 | let mut next_floor = floor_requests.pop_request(); 37 | while let Some(dst) = next_floor 38 | { 39 | //5.1. Update location, velocity, and acceleration 40 | let now = Instant::now(); 41 | let ts = now.duration_since(original_ts) 42 | .as_fractional_secs(); 43 | let dt = ts - est.timestamp; 44 | est.timestamp = ts; 45 | 46 | est.location = est.location + est.velocity * dt; 47 | est.velocity = est.velocity + est.acceleration * dt; 48 | est.acceleration = { 49 | let F = est.motor_input; 50 | let m = esp.get_carriage_weight(); 51 | -9.8 + F/m 52 | }; 53 | 54 | //5.2. If next floor request in queue is satisfied, then remove from queue 55 | if (est.location - getCumulativeFloorHeight(esp.get_floor_heights(), dst)).abs() < 0.01 && 56 | est.velocity.abs() < 0.01 57 | { 58 | est.velocity = 0.0; 59 | next_floor = floor_requests.pop_request(); 60 | } 61 | 62 | //5.4. Print realtime statistics 63 | dr.record(est.clone(), dst); 64 | 65 | //5.3. Adjust motor control to process next floor request 66 | est.motor_input = mc.adjust(&est, dst); 67 | 68 | thread::sleep(time::Duration::from_millis(1)); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /Chapter05/src/simulate_trip.rs: -------------------------------------------------------------------------------- 1 | extern crate elevator; 2 | extern crate floating_duration; 3 | 4 | use elevator::buildings::{Building, Building1, Building2, Building3}; 5 | use elevator::trip_planning::{FloorRequests, RequestQueue}; 6 | use elevator::physics::{ElevatorState, simulate_elevator}; 7 | use elevator::motion_controllers::{SmoothMotionController, MotionController}; 8 | use elevator::data_recorders::{newSimpleDataRecorder, DataRecorder}; 9 | 10 | use std::collections::VecDeque; 11 | use std::time::Instant; 12 | use std::env; 13 | use std::fs::File; 14 | use std::io::{self, Read, Write}; 15 | use std::io::prelude::*; 16 | use std::cmp; 17 | 18 | pub fn run_simulation() 19 | { 20 | 21 | //1. Store location, velocity, and acceleration state 22 | //2. Store motor input target force 23 | let mut est = ElevatorState { 24 | timestamp: 0.0, 25 | location: 0.0, 26 | velocity: 0.0, 27 | acceleration: 0.0, 28 | motor_input: 0.0 29 | }; 30 | 31 | //3. Store input building description and floor requests 32 | let mut esp: Box = Box::new(Building1); 33 | let mut floor_requests: Box = Box::new(FloorRequests { 34 | requests: VecDeque::new() 35 | }); 36 | 37 | //4. Parse input and store as building description and floor requests 38 | match env::args().nth(1) { 39 | Some(ref fp) if *fp == "-".to_string() => { 40 | let mut buffer = String::new(); 41 | io::stdin().read_to_string(&mut buffer) 42 | .expect("read_to_string failed"); 43 | 44 | for (li,l) in buffer.lines().enumerate() { 45 | if li==0 { 46 | let building = l.parse::().unwrap(); 47 | if building==0 { 48 | esp = Box::new(Building1); 49 | } else if building==1 { 50 | esp = Box::new(Building2); 51 | } else if building==2 { 52 | esp = Box::new(Building3); 53 | } else { 54 | panic!("unknown building code: {}", building); 55 | } 56 | } else { 57 | floor_requests.add_request(l.parse::().unwrap()); 58 | } 59 | } 60 | }, 61 | None => { 62 | let fp = "test1.txt"; 63 | let mut buffer = String::new(); 64 | File::open(fp) 65 | .expect("File::open failed") 66 | .read_to_string(&mut buffer) 67 | .expect("read_to_string failed"); 68 | 69 | for (li,l) in buffer.lines().enumerate() { 70 | if li==0 { 71 | let building = l.parse::().unwrap(); 72 | if building==0 { 73 | esp = Box::new(Building1); 74 | } else if building==1 { 75 | esp = Box::new(Building2); 76 | } else if building==2 { 77 | esp = Box::new(Building3); 78 | } else { 79 | panic!("unknown building code: {}", building); 80 | } 81 | } else { 82 | floor_requests.add_request(l.parse::().unwrap()); 83 | } 84 | } 85 | }, 86 | Some(fp) => { 87 | let mut buffer = String::new(); 88 | File::open(fp) 89 | .expect("File::open failed") 90 | .read_to_string(&mut buffer) 91 | .expect("read_to_string failed"); 92 | 93 | for (li,l) in buffer.lines().enumerate() { 94 | if li==0 { 95 | let building = l.parse::().unwrap(); 96 | if building==0 { 97 | esp = Box::new(Building1); 98 | } else if building==1 { 99 | esp = Box::new(Building2); 100 | } else if building==2 { 101 | esp = Box::new(Building3); 102 | } else { 103 | panic!("unknown building code: {}", building); 104 | } 105 | } else { 106 | floor_requests.add_request(l.parse::().unwrap()); 107 | } 108 | } 109 | } 110 | } 111 | 112 | let mut dr: Box = newSimpleDataRecorder(esp.clone()); 113 | let mut mc: Box = Box::new(SmoothMotionController { 114 | timestamp: 0.0, 115 | esp: esp.clone() 116 | }); 117 | 118 | simulate_elevator(esp, est, &mut floor_requests, &mut mc, &mut dr); 119 | dr.summary(); 120 | 121 | } 122 | 123 | fn main() 124 | { 125 | run_simulation() 126 | } 127 | -------------------------------------------------------------------------------- /Chapter05/src/trip_planning.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | 3 | pub struct FloorRequests 4 | { 5 | pub requests: VecDeque 6 | } 7 | 8 | pub trait RequestQueue 9 | { 10 | fn add_request(&mut self, req: u64); 11 | fn add_requests(&mut self, reqs: &Vec); 12 | fn pop_request(&mut self) -> Option; 13 | } 14 | 15 | impl RequestQueue for FloorRequests 16 | { 17 | fn add_request(&mut self, req: u64) 18 | { 19 | self.requests.push_back(req); 20 | } 21 | fn add_requests(&mut self, reqs: &Vec) 22 | { 23 | for req in reqs 24 | { 25 | self.requests.push_back(*req); 26 | } 27 | } 28 | fn pop_request(&mut self) -> Option 29 | { 30 | self.requests.pop_front() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Chapter05/test1.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 1 3 | 2 4 | -------------------------------------------------------------------------------- /Chapter06/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "elevator_coding" 3 | version = "1.0.0" 4 | build = "build.rs" 5 | 6 | [dependencies] 7 | lazy_static = "1" 8 | cached = "0.5" 9 | floating-duration = "0.1.2" 10 | termion = "1.0" 11 | timebomb = "0.1" 12 | serde = "1.0" 13 | serde_json = "1.0" 14 | serde_derive = "1.0" 15 | libc = "0.2" 16 | 17 | [[bin]] 18 | name = "mutability" 19 | path = "mutability.rs" 20 | 21 | [[bin]] 22 | name = "pure_functions" 23 | path = "pure_functions.rs" 24 | -------------------------------------------------------------------------------- /Chapter06/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | use std::env; 3 | use std::path::Path; 4 | 5 | fn main() { 6 | let out_dir = env::var("OUT_DIR").unwrap(); 7 | 8 | Command::new("gcc").args(&["src/elevator_magic.c", "-c", "-fPIC", "-o"]) 9 | .arg(&format!("{}/elevator_magic.o", out_dir)) 10 | .status().unwrap(); 11 | Command::new("ar").args(&["crus", "libelevatormagic.a", "elevator_magic.o"]) 12 | .current_dir(&Path::new(&out_dir)) 13 | .status().unwrap(); 14 | 15 | println!("cargo:rustc-link-search=native={}", out_dir); 16 | println!("cargo:rustc-link-lib=static=elevatormagic"); 17 | } 18 | -------------------------------------------------------------------------------- /Chapter06/fixed/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | -------------------------------------------------------------------------------- /Chapter06/fixed/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "elevator_coding" 3 | version = "1.0.0" 4 | build = "build.rs" 5 | 6 | [dependencies] 7 | floating-duration = "0.1.2" 8 | termion = "1.0" 9 | timebomb = "0.1" 10 | serde = "1.0" 11 | serde_json = "1.0" 12 | serde_derive = "1.0" 13 | libc = "0.2" 14 | -------------------------------------------------------------------------------- /Chapter06/fixed/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | use std::env; 3 | use std::path::Path; 4 | 5 | fn main() { 6 | let out_dir = env::var("OUT_DIR").unwrap(); 7 | 8 | Command::new("gcc").args(&["src/elevator_magic.c", "-c", "-fPIC", "-o"]) 9 | .arg(&format!("{}/elevator_magic.o", out_dir)) 10 | .status().unwrap(); 11 | Command::new("ar").args(&["crus", "libelevatormagic.a", "elevator_magic.o"]) 12 | .current_dir(&Path::new(&out_dir)) 13 | .status().unwrap(); 14 | 15 | println!("cargo:rustc-link-search=native={}", out_dir); 16 | println!("cargo:rustc-link-lib=static=elevatormagic"); 17 | } 18 | -------------------------------------------------------------------------------- /Chapter06/fixed/src/admin.rs: -------------------------------------------------------------------------------- 1 | use magic; 2 | use libc::c_void; 3 | use std::rc::Rc; 4 | 5 | pub enum OverrideCode { 6 | IssueOverride = 1, 7 | IssuePrivileged = 2, 8 | IssueAdmin = 3, 9 | IssueInputFloor = 4, 10 | IssueManualMode = 5, 11 | IssueNormalMode = 6, 12 | IssueFlash = 7, 13 | IssueToggleLight = 8, 14 | IssueSetLightColor = 9, 15 | } 16 | pub fn toOverrideCode(i: i32) -> OverrideCode { 17 | match i { 18 | 1 => OverrideCode::IssueOverride, 19 | 2 => OverrideCode::IssuePrivileged, 20 | 3 => OverrideCode::IssueAdmin, 21 | 4 => OverrideCode::IssueInputFloor, 22 | 5 => OverrideCode::IssueManualMode, 23 | 6 => OverrideCode::IssueNormalMode, 24 | 7 => OverrideCode::IssueFlash, 25 | 8 => OverrideCode::IssueToggleLight, 26 | 9 => OverrideCode::IssueSetLightColor, 27 | _ => panic!("Unexpected override code: {}", i) 28 | } 29 | } 30 | 31 | pub enum ErrorCode { 32 | DoubleAuthorize = 1, 33 | DoubleFree = 2, 34 | AccessDenied = 3, 35 | } 36 | pub fn toErrorCode(i: i32) -> ErrorCode { 37 | match i { 38 | 1 => ErrorCode::DoubleAuthorize, 39 | 2 => ErrorCode::DoubleFree, 40 | 3 => ErrorCode::AccessDenied, 41 | _ => panic!("Unexpected error code: {}", i) 42 | } 43 | } 44 | 45 | struct AuthorizedSessionInner(*const c_void); 46 | 47 | #[derive(Clone)] 48 | pub struct AuthorizedSession 49 | { 50 | session: Rc 51 | } 52 | impl Drop for AuthorizedSessionInner { 53 | fn drop(&mut self) { 54 | unsafe { 55 | magic::free_override_session(self.0); 56 | } 57 | } 58 | } 59 | 60 | pub fn authorize_override() -> Result { 61 | if is_override() || is_privileged() || is_admin() { 62 | return Result::Err(ErrorCode::DoubleAuthorize) 63 | } 64 | let session = unsafe { 65 | magic::issue_override_code(OverrideCode::IssueOverride as i32); 66 | magic::poll_override_session() 67 | }; 68 | let session = AuthorizedSession { 69 | session: Rc::new(AuthorizedSessionInner(session)) 70 | }; 71 | check_error(session) 72 | } 73 | 74 | pub fn authorize_privileged() -> Result { 75 | if is_override() || is_privileged() || is_admin() { 76 | return Result::Err(ErrorCode::DoubleAuthorize) 77 | } 78 | let session = unsafe { 79 | magic::issue_override_code(OverrideCode::IssuePrivileged as i32); 80 | magic::poll_physical_override_privileged_session() 81 | }; 82 | let session = AuthorizedSession { 83 | session: Rc::new(AuthorizedSessionInner(session)) 84 | }; 85 | check_error(session) 86 | } 87 | 88 | pub fn authorize_admin() -> Result { 89 | if is_override() || is_privileged() || is_admin() { 90 | return Result::Err(ErrorCode::DoubleAuthorize) 91 | } 92 | let session = unsafe { 93 | magic::issue_override_code(OverrideCode::IssueAdmin as i32); 94 | magic::poll_physical_override_admin_session() 95 | }; 96 | let session = AuthorizedSession { 97 | session: Rc::new(AuthorizedSessionInner(session)) 98 | }; 99 | check_error(session) 100 | } 101 | 102 | pub fn reset_state() 103 | { 104 | unsafe { 105 | magic::override_reset_state(); 106 | } 107 | } 108 | 109 | pub fn check_error(t: T) -> Result 110 | { 111 | let err = unsafe { 112 | magic::poll_override_error() 113 | }; 114 | if err==0 { 115 | Result::Ok(t) 116 | } else { 117 | Result::Err(toErrorCode(err)) 118 | } 119 | } 120 | 121 | pub fn input_floor(floor: i32) -> Result<(),ErrorCode> 122 | { 123 | unsafe { 124 | magic::override_input_floor(floor); 125 | } 126 | check_error(()) 127 | } 128 | 129 | pub fn manual_mode() -> Result<(),ErrorCode> 130 | { 131 | unsafe { 132 | magic::override_manual_mode(); 133 | } 134 | check_error(()) 135 | } 136 | 137 | pub fn normal_mode() -> Result<(),ErrorCode> 138 | { 139 | unsafe { 140 | magic::override_normal_mode(); 141 | } 142 | check_error(()) 143 | } 144 | 145 | pub fn flash(pattern: i32) -> Result<(),ErrorCode> 146 | { 147 | unsafe { 148 | magic::elevator_display_flash(pattern); 149 | } 150 | check_error(()) 151 | } 152 | pub fn toggle_light(light_id: i32) -> Result<(),ErrorCode> 153 | { 154 | unsafe { 155 | magic::elevator_display_toggle_light(light_id); 156 | } 157 | check_error(()) 158 | } 159 | pub fn set_light_color(light_id: i32, color: i32) -> Result<(),ErrorCode> 160 | { 161 | unsafe { 162 | magic::elevator_display_set_light_color(light_id, color); 163 | } 164 | check_error(()) 165 | } 166 | 167 | pub fn is_override() -> bool 168 | { 169 | unsafe { 170 | magic::is_override() != 0 171 | } 172 | } 173 | 174 | pub fn is_privileged() -> bool 175 | { 176 | unsafe { 177 | magic::is_privileged() != 0 178 | } 179 | } 180 | 181 | pub fn is_admin() -> bool 182 | { 183 | unsafe { 184 | magic::is_admin() != 0 185 | } 186 | } 187 | 188 | -------------------------------------------------------------------------------- /Chapter06/fixed/src/elevator_magic.c: -------------------------------------------------------------------------------- 1 | int input_codes[3] = {0, 0, 0}; 2 | int error_code = 0; 3 | void *active_session = 0; 4 | void *poll_session = 0; 5 | int active_light = 0; 6 | int active_light_toggle = 0; 7 | 8 | int is_override() { 9 | return (active_session == ((void*)1)); 10 | } 11 | 12 | int is_privileged() { 13 | return (active_session == ((void*)2)); 14 | } 15 | 16 | int is_admin() { 17 | return (active_session == ((void*)3)); 18 | } 19 | 20 | void issue_override_code(int code) { 21 | input_codes[0] = 0; 22 | input_codes[1] = 0; 23 | input_codes[2] = 0; 24 | input_codes[0] = code; 25 | if( code == 1 ) { 26 | if( active_session == ((void*)0) ) { 27 | active_session = (void*)1; 28 | poll_session = (void*)1; 29 | } else { 30 | active_session = (void*)4; 31 | poll_session = (void*)0; 32 | error_code = 1; 33 | } 34 | } else if( code == 2 ) { 35 | if( active_session == ((void*)0) ) { 36 | active_session = (void*)2; 37 | poll_session = (void*)2; 38 | } else { 39 | active_session = (void*)4; 40 | poll_session = (void*)0; 41 | error_code = 1; 42 | } 43 | } else if( code == 3 ) { 44 | if( active_session == ((void*)0) ) { 45 | active_session = (void*)3; 46 | poll_session = (void*)3; 47 | } else { 48 | active_session = (void*)4; 49 | poll_session = (void*)0; 50 | error_code = 1; 51 | } 52 | } 53 | } 54 | 55 | int poll_override_code() { 56 | int code = input_codes[0]; 57 | input_codes[0] = input_codes[1]; 58 | input_codes[1] = input_codes[2]; 59 | input_codes[2] = 0; 60 | return code; 61 | } 62 | 63 | int poll_override_input_floor() { 64 | //C Code 65 | return 0; 66 | } 67 | 68 | int poll_override_error() { 69 | return error_code; 70 | } 71 | 72 | void* poll_override_session() { 73 | return poll_session; 74 | } 75 | 76 | void free_override_session(void* session) { 77 | if( active_session == 0 ) { 78 | error_code = 2; 79 | } 80 | active_session = 0; 81 | } 82 | 83 | void* poll_physical_override_privileged_session() { 84 | return poll_session; 85 | } 86 | 87 | void* poll_physical_override_admin_session() { 88 | return poll_session; 89 | } 90 | 91 | void override_input_floor(int floor) { 92 | if(active_session) { 93 | input_codes[0] = 4; 94 | input_codes[1] = floor; 95 | input_codes[2] = 0; 96 | } else { 97 | error_code = 3; 98 | } 99 | } 100 | 101 | void override_manual_mode() { 102 | if(active_session) { 103 | input_codes[0] = 5; 104 | input_codes[1] = 0; 105 | input_codes[2] = 0; 106 | } else { 107 | error_code = 3; 108 | } 109 | } 110 | 111 | void override_normal_mode() { 112 | if(active_session) { 113 | input_codes[0] = 6; 114 | input_codes[1] = 0; 115 | input_codes[2] = 0; 116 | } else { 117 | error_code = 3; 118 | } 119 | } 120 | 121 | void override_reset_state() { 122 | input_codes[0] = 0; 123 | input_codes[1] = 0; 124 | input_codes[2] = 0; 125 | error_code = 0; 126 | active_session = 0; 127 | poll_session = 0; 128 | active_light = 0; 129 | active_light_toggle = 0; 130 | } 131 | 132 | void elevator_display_flash(int pattern) { 133 | input_codes[0] = 7; 134 | input_codes[1] = pattern; 135 | input_codes[2] = 0; 136 | } 137 | 138 | void elevator_display_toggle_light(int light_id) { 139 | int last_state = 0; 140 | if( active_light == light_id ) { 141 | last_state = active_light_toggle; 142 | } 143 | active_light = light_id; 144 | active_light_toggle = last_state ? 0 : 1; 145 | input_codes[0] = 8; 146 | input_codes[1] = light_id; 147 | input_codes[2] = active_light_toggle; 148 | } 149 | 150 | void elevator_display_set_light_color(int light_id, int color) { 151 | input_codes[0] = 9; 152 | input_codes[1] = light_id; 153 | input_codes[2] = color; 154 | } 155 | -------------------------------------------------------------------------------- /Chapter06/fixed/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | extern crate libc; 3 | 4 | mod magic; 5 | mod admin; 6 | mod tests; 7 | -------------------------------------------------------------------------------- /Chapter06/fixed/src/magic.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_int, c_void}; 2 | 3 | #[link(name = "elevatormagic")] 4 | extern { 5 | pub fn issue_override_code(code: c_int); 6 | pub fn poll_override_code() -> c_int; 7 | pub fn poll_override_input_floor() -> c_int; 8 | pub fn poll_override_error() -> c_int; 9 | pub fn poll_override_session() -> *const c_void; 10 | pub fn free_override_session(session: *const c_void); 11 | pub fn poll_physical_override_privileged_session() -> *const c_void; 12 | pub fn poll_physical_override_admin_session() -> *const c_void; 13 | pub fn override_input_floor(floor: c_int); 14 | pub fn override_manual_mode(); 15 | pub fn override_normal_mode(); 16 | pub fn override_reset_state(); 17 | pub fn elevator_display_flash(pattern: c_int); 18 | pub fn elevator_display_toggle_light(light_id: c_int); 19 | pub fn elevator_display_set_light_color(light_id: c_int, color: c_int); 20 | pub fn is_override() -> c_int; 21 | pub fn is_privileged() -> c_int; 22 | pub fn is_admin() -> c_int; 23 | } 24 | -------------------------------------------------------------------------------- /Chapter06/fixed/src/tests/admin.rs: -------------------------------------------------------------------------------- 1 | use admin; 2 | 3 | #[test] 4 | fn authorize_override() { 5 | admin::reset_state(); 6 | { 7 | let session = admin::authorize_override().ok(); 8 | assert!(admin::is_override()); 9 | } 10 | assert!(!admin::is_override()); 11 | assert!(admin::check_error(()).is_ok()); 12 | } 13 | #[test] 14 | fn authorize_privileged() { 15 | admin::reset_state(); 16 | { 17 | let session = admin::authorize_privileged().ok(); 18 | assert!(admin::is_privileged()); 19 | } 20 | assert!(!admin::is_privileged()); 21 | assert!(admin::check_error(()).is_ok()); 22 | } 23 | #[test] 24 | fn issue_admin_code() { 25 | admin::reset_state(); 26 | { 27 | let session = admin::authorize_admin().ok(); 28 | assert!(admin::is_admin()); 29 | } 30 | assert!(!admin::is_admin()); 31 | assert!(admin::check_error(()).is_ok()); 32 | } 33 | 34 | #[test] 35 | fn double_override_failure() { 36 | admin::reset_state(); 37 | let session = admin::authorize_override().ok(); 38 | assert!(admin::authorize_override().err().is_some()); 39 | } 40 | #[test] 41 | fn double_privileged_failure() { 42 | admin::reset_state(); 43 | let session = admin::authorize_privileged().ok(); 44 | assert!(admin::authorize_privileged().err().is_some()); 45 | } 46 | #[test] 47 | fn double_admin_failure() { 48 | admin::reset_state(); 49 | let session = admin::authorize_admin().ok(); 50 | assert!(admin::authorize_admin().err().is_some()); 51 | } 52 | 53 | #[test] 54 | fn clone_override() { 55 | admin::reset_state(); 56 | { 57 | let session = admin::authorize_override().ok().unwrap(); 58 | let session2 = session.clone(); 59 | assert!(admin::is_override()); 60 | } 61 | assert!(!admin::is_override()); 62 | assert!(admin::check_error(()).is_ok()); 63 | } 64 | #[test] 65 | fn clone_privileged() { 66 | admin::reset_state(); 67 | { 68 | let session = admin::authorize_privileged().ok().unwrap(); 69 | let session2 = session.clone(); 70 | assert!(admin::is_privileged()); 71 | } 72 | assert!(!admin::is_privileged()); 73 | assert!(admin::check_error(()).is_ok()); 74 | } 75 | #[test] 76 | fn clone_admin() { 77 | admin::reset_state(); 78 | { 79 | let session = admin::authorize_admin().ok().unwrap(); 80 | let session2 = session.clone(); 81 | assert!(admin::is_admin()); 82 | } 83 | assert!(!admin::is_admin()); 84 | assert!(admin::check_error(()).is_ok()); 85 | } 86 | 87 | #[test] 88 | fn input_floor() { 89 | admin::reset_state(); 90 | { 91 | let session = admin::authorize_admin().ok(); 92 | admin::input_floor(2).ok(); 93 | } 94 | assert!(!admin::is_admin()); 95 | assert!(admin::check_error(()).is_ok()); 96 | } 97 | 98 | #[test] 99 | fn manual_mode() { 100 | admin::reset_state(); 101 | { 102 | let session = admin::authorize_admin().ok(); 103 | admin::manual_mode().ok(); 104 | } 105 | assert!(!admin::is_admin()); 106 | assert!(admin::check_error(()).is_ok()); 107 | } 108 | 109 | #[test] 110 | fn normal_mode() { 111 | admin::reset_state(); 112 | { 113 | let session = admin::authorize_admin().ok(); 114 | admin::normal_mode().ok(); 115 | } 116 | assert!(!admin::is_admin()); 117 | assert!(admin::check_error(()).is_ok()); 118 | } 119 | 120 | #[test] 121 | fn flash() { 122 | admin::reset_state(); 123 | assert!(!admin::is_override()); 124 | assert!(!admin::is_privileged()); 125 | assert!(!admin::is_admin()); 126 | admin::flash(222).ok(); 127 | assert!(admin::check_error(()).is_ok()); 128 | } 129 | 130 | #[test] 131 | fn toggle_light() { 132 | admin::reset_state(); 133 | assert!(!admin::is_override()); 134 | assert!(!admin::is_privileged()); 135 | assert!(!admin::is_admin()); 136 | admin::toggle_light(7).ok(); 137 | assert!(admin::check_error(()).is_ok()); 138 | } 139 | 140 | #[test] 141 | fn set_light_color() { 142 | admin::reset_state(); 143 | assert!(!admin::is_override()); 144 | assert!(!admin::is_privileged()); 145 | assert!(!admin::is_admin()); 146 | admin::set_light_color(33, 123).ok(); 147 | assert!(admin::check_error(()).is_ok()); 148 | } 149 | 150 | #[test] 151 | fn deny_input_floor() { 152 | admin::reset_state(); 153 | admin::input_floor(2).err(); 154 | assert!(!admin::check_error(()).is_ok()); 155 | } 156 | 157 | #[test] 158 | fn deny_manual_mode() { 159 | admin::reset_state(); 160 | admin::manual_mode().err(); 161 | assert!(!admin::check_error(()).is_ok()); 162 | } 163 | 164 | #[test] 165 | fn deny_normal_mode() { 166 | admin::reset_state(); 167 | admin::normal_mode().err(); 168 | assert!(!admin::check_error(()).is_ok()); 169 | } 170 | 171 | #[test] 172 | fn invalid_deauthorization() { 173 | admin::reset_state(); 174 | let session = admin::authorize_admin().ok(); 175 | assert!(admin::authorize_admin().is_err()); 176 | assert!(admin::is_admin()); 177 | } 178 | -------------------------------------------------------------------------------- /Chapter06/fixed/src/tests/magic.rs: -------------------------------------------------------------------------------- 1 | use magic; 2 | use libc::{c_void}; 3 | 4 | #[test] 5 | fn issue_override_code() { 6 | unsafe { 7 | magic::override_reset_state(); 8 | magic::issue_override_code(1); 9 | assert!(magic::poll_override_code() == 1); 10 | assert!(magic::poll_override_error() == 0); 11 | } 12 | } 13 | #[test] 14 | fn issue_privileged_code() { 15 | unsafe { 16 | magic::override_reset_state(); 17 | magic::issue_override_code(2); 18 | assert!(magic::poll_override_code() == 2); 19 | assert!(magic::poll_override_error() == 0); 20 | } 21 | } 22 | #[test] 23 | fn issue_admin_code() { 24 | unsafe { 25 | magic::override_reset_state(); 26 | magic::issue_override_code(3); 27 | assert!(magic::poll_override_code() == 3); 28 | assert!(magic::poll_override_error() == 0); 29 | } 30 | } 31 | 32 | #[test] 33 | fn authorize_override_success() { 34 | unsafe { 35 | magic::override_reset_state(); 36 | magic::issue_override_code(1); 37 | let session = magic::poll_override_session(); 38 | assert!(session != (0 as *const c_void)); 39 | magic::free_override_session(session); 40 | assert!(magic::poll_override_error() == 0); 41 | } 42 | } 43 | #[test] 44 | fn authorize_privileged_success() { 45 | unsafe { 46 | magic::override_reset_state(); 47 | magic::issue_override_code(2); 48 | let session = magic::poll_physical_override_privileged_session(); 49 | assert!(session != (0 as *const c_void)); 50 | magic::free_override_session(session); 51 | assert!(magic::poll_override_error() == 0); 52 | } 53 | } 54 | #[test] 55 | fn authorize_admin_success() { 56 | unsafe { 57 | magic::override_reset_state(); 58 | magic::issue_override_code(3); 59 | let session = magic::poll_physical_override_admin_session(); 60 | assert!(session != (0 as *const c_void)); 61 | magic::free_override_session(session); 62 | assert!(magic::poll_override_error() == 0); 63 | } 64 | } 65 | 66 | #[test] 67 | fn double_override_failure() { 68 | unsafe { 69 | magic::override_reset_state(); 70 | magic::issue_override_code(1); 71 | magic::issue_override_code(1); 72 | assert!(magic::poll_override_session() == (0 as *const c_void)); 73 | assert!(magic::poll_override_error() == 1); 74 | } 75 | } 76 | #[test] 77 | fn double_privileged_failure() { 78 | unsafe { 79 | magic::override_reset_state(); 80 | magic::issue_override_code(2); 81 | magic::issue_override_code(2); 82 | assert!(magic::poll_physical_override_privileged_session() == (0 as *const c_void)); 83 | assert!(magic::poll_override_error() == 1); 84 | } 85 | } 86 | #[test] 87 | fn double_admin_failure() { 88 | unsafe { 89 | magic::override_reset_state(); 90 | magic::issue_override_code(3); 91 | magic::issue_override_code(3); 92 | assert!(magic::poll_physical_override_admin_session() == (0 as *const c_void)); 93 | assert!(magic::poll_override_error() == 1); 94 | } 95 | } 96 | 97 | #[test] 98 | fn double_free_override_failure() { 99 | unsafe { 100 | magic::override_reset_state(); 101 | magic::issue_override_code(1); 102 | let session = magic::poll_override_session(); 103 | assert!(session != (0 as *const c_void)); 104 | magic::free_override_session(session); 105 | magic::free_override_session(session); 106 | assert!(magic::poll_override_error() == 2); 107 | } 108 | } 109 | #[test] 110 | fn double_free_privileged_failure() { 111 | unsafe { 112 | magic::override_reset_state(); 113 | magic::issue_override_code(2); 114 | let session = magic::poll_physical_override_privileged_session(); 115 | assert!(session != (0 as *const c_void)); 116 | magic::free_override_session(session); 117 | magic::free_override_session(session); 118 | assert!(magic::poll_override_error() == 2); 119 | } 120 | } 121 | #[test] 122 | fn double_free_admin_failure() { 123 | unsafe { 124 | magic::override_reset_state(); 125 | magic::issue_override_code(3); 126 | let session = magic::poll_physical_override_admin_session(); 127 | assert!(session != (0 as *const c_void)); 128 | magic::free_override_session(session); 129 | magic::free_override_session(session); 130 | assert!(magic::poll_override_error() == 2); 131 | } 132 | } 133 | 134 | #[test] 135 | fn input_floor() { 136 | unsafe { 137 | magic::override_reset_state(); 138 | magic::issue_override_code(3); 139 | magic::override_input_floor(2); 140 | assert!(magic::poll_override_code() == 4); 141 | assert!(magic::poll_override_code() == 2); 142 | assert!(magic::poll_override_error() == 0); 143 | } 144 | } 145 | 146 | #[test] 147 | fn manual_mode() { 148 | unsafe { 149 | magic::override_reset_state(); 150 | magic::issue_override_code(3); 151 | magic::override_manual_mode(); 152 | assert!(magic::poll_override_code() == 5); 153 | assert!(magic::poll_override_error() == 0); 154 | } 155 | } 156 | 157 | #[test] 158 | fn normal_mode() { 159 | unsafe { 160 | magic::override_reset_state(); 161 | magic::issue_override_code(3); 162 | magic::override_normal_mode(); 163 | assert!(magic::poll_override_code() == 6); 164 | assert!(magic::poll_override_error() == 0); 165 | } 166 | } 167 | 168 | #[test] 169 | fn flash() { 170 | unsafe { 171 | magic::override_reset_state(); 172 | magic::elevator_display_flash(222); 173 | assert!(magic::poll_override_code() == 7); 174 | assert!(magic::poll_override_code() == 222); 175 | } 176 | } 177 | 178 | #[test] 179 | fn toggle_light() { 180 | unsafe { 181 | magic::override_reset_state(); 182 | magic::elevator_display_toggle_light(33); 183 | assert!(magic::poll_override_code() == 8); 184 | assert!(magic::poll_override_code() == 33); 185 | assert!(magic::poll_override_code() == 1); 186 | magic::elevator_display_toggle_light(33); 187 | assert!(magic::poll_override_code() == 8); 188 | assert!(magic::poll_override_code() == 33); 189 | assert!(magic::poll_override_code() == 0); 190 | } 191 | } 192 | 193 | #[test] 194 | fn set_light_color() { 195 | unsafe { 196 | magic::override_reset_state(); 197 | magic::elevator_display_set_light_color(33, 222); 198 | assert!(magic::poll_override_code() == 9); 199 | assert!(magic::poll_override_code() == 33); 200 | assert!(magic::poll_override_code() == 222); 201 | } 202 | } 203 | 204 | #[test] 205 | fn deny_input_floor() { 206 | unsafe { 207 | magic::override_reset_state(); 208 | magic::override_input_floor(2); 209 | assert!(magic::poll_override_error() == 3); 210 | } 211 | } 212 | 213 | #[test] 214 | fn deny_manual_mode() { 215 | unsafe { 216 | magic::override_reset_state(); 217 | magic::override_manual_mode(); 218 | assert!(magic::poll_override_error() == 3); 219 | } 220 | } 221 | 222 | #[test] 223 | fn deny_normal_mode() { 224 | unsafe { 225 | magic::override_reset_state(); 226 | magic::override_manual_mode(); 227 | assert!(magic::poll_override_error() == 3); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /Chapter06/fixed/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | mod magic; 3 | mod admin; 4 | -------------------------------------------------------------------------------- /Chapter06/mutability.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Mutex, Arc}; 2 | 3 | fn f(x: &mut i32) { 4 | *x = 2; 5 | } 6 | 7 | #[derive(Clone)] 8 | struct TimeBomb { 9 | countdown: Arc> 10 | } 11 | impl Drop for TimeBomb 12 | { 13 | fn drop(&mut self) { 14 | let mut c = self.countdown.lock().unwrap(); 15 | *c -= 1; 16 | if *c <= 0 { 17 | panic!("BOOM!!") 18 | } 19 | } 20 | } 21 | 22 | fn main() 23 | { 24 | let a = 5; 25 | let mut b = 5; 26 | 27 | //a = 4; not valid 28 | b = 4; 29 | 30 | //*(&mut a) = 3; not valid 31 | *(&mut b) = 3; 32 | 33 | let a = 5; 34 | let mut b = 5; 35 | 36 | //f(&mut a); not valid 37 | f(&mut b); 38 | 39 | { 40 | let t3 = TimeBomb { 41 | countdown: Arc::new(Mutex::new(3)) 42 | }; 43 | let t2 = t3.clone(); 44 | let t1 = t2.clone(); 45 | let t0 = t1.clone(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Chapter06/pure_functions.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate lazy_static; 2 | #[macro_use] extern crate cached; 3 | use std::collections::HashMap; 4 | use std::sync::{Arc,Mutex}; 5 | use std::cell::Cell; 6 | 7 | fn p0() {} 8 | 9 | fn p1() -> u64 { 10 | 444 11 | } 12 | 13 | fn p2(x: u64) -> u64 { 14 | x * 444 15 | } 16 | 17 | fn p3(x: u64, y: u64) -> u64 { 18 | x * 444 + y 19 | } 20 | 21 | static mut blah: u64 = 3; 22 | fn ip0() { 23 | unsafe { 24 | blah = 444; 25 | } 26 | } 27 | 28 | fn ip1(c: &Cell) { 29 | c.set(333); 30 | } 31 | 32 | cached!{ 33 | FIB; 34 | fn fib(n: u64) -> u64 = { 35 | if n == 0 || n == 1 { return n } 36 | fib(n-1) + fib(n-2) 37 | } 38 | } 39 | 40 | lazy_static! { 41 | static ref BUCKET_COUNTER: Mutex> = { 42 | Mutex::new(HashMap::new()) 43 | }; 44 | } 45 | cached!{ 46 | BUCK; 47 | fn bucket_count(n: u64) -> u64 = { 48 | let mut counter = BUCKET_COUNTER.lock().unwrap(); 49 | let r = match counter.get(&n) { 50 | Some(c) => { c+1 } 51 | None => { 1 } 52 | }; 53 | counter.insert(n, r); 54 | r 55 | } 56 | } 57 | 58 | #[derive(Clone)] 59 | pub struct TimeBomb { 60 | countdown: Arc> 61 | } 62 | impl Drop for TimeBomb 63 | { 64 | fn drop(&mut self) { 65 | let mut c = self.countdown.lock().unwrap(); 66 | *c -= 1; 67 | if *c <= 0 { 68 | panic!("BOOM!!") 69 | } 70 | } 71 | } 72 | cached!{ 73 | TICKING_BOX; 74 | fn tick_tock(v: i32) -> TimeBomb = { 75 | TimeBomb { 76 | countdown: Arc::new(Mutex::new(v)) 77 | } 78 | } 79 | } 80 | 81 | fn main() 82 | { 83 | p0(); 84 | p1(); 85 | p2(3); 86 | p3(3,4); 87 | 88 | ip0(); 89 | let r = Cell::new(3); 90 | ip1(&r); 91 | ip1(&r); 92 | 93 | fib(30); //call 1, generates correct value and returns it 94 | fib(30); //call 2, finds correct value and returns it 95 | 96 | bucket_count(30); //call 1, generates correct value and returns it 97 | bucket_count(30); //call 2, finds stale value and returns it 98 | 99 | tick_tock(3); 100 | tick_tock(3); 101 | tick_tock(3); 102 | } 103 | -------------------------------------------------------------------------------- /Chapter06/src/admin.rs: -------------------------------------------------------------------------------- 1 | use magic; 2 | use libc::c_void; 3 | 4 | pub enum OverrideCode { 5 | IssueOverride = 1, 6 | IssuePrivileged = 2, 7 | IssueAdmin = 3, 8 | IssueInputFloor = 4, 9 | IssueManualMode = 5, 10 | IssueNormalMode = 6, 11 | IssueFlash = 7, 12 | IssueToggleLight = 8, 13 | IssueSetLightColor = 9, 14 | } 15 | pub fn toOverrideCode(i: i32) -> OverrideCode { 16 | match i { 17 | 1 => OverrideCode::IssueOverride, 18 | 2 => OverrideCode::IssuePrivileged, 19 | 3 => OverrideCode::IssueAdmin, 20 | 4 => OverrideCode::IssueInputFloor, 21 | 5 => OverrideCode::IssueManualMode, 22 | 6 => OverrideCode::IssueNormalMode, 23 | 7 => OverrideCode::IssueFlash, 24 | 8 => OverrideCode::IssueToggleLight, 25 | 9 => OverrideCode::IssueSetLightColor, 26 | _ => panic!("Unexpected override code: {}", i) 27 | } 28 | } 29 | 30 | pub enum ErrorCode { 31 | DoubleAuthorize = 1, 32 | DoubleFree = 2, 33 | AccessDenied = 3, 34 | } 35 | pub fn toErrorCode(i: i32) -> ErrorCode { 36 | match i { 37 | 1 => ErrorCode::DoubleAuthorize, 38 | 2 => ErrorCode::DoubleFree, 39 | 3 => ErrorCode::AccessDenied, 40 | _ => panic!("Unexpected error code: {}", i) 41 | } 42 | } 43 | 44 | #[derive(Clone)] 45 | pub struct AuthorizedSession 46 | { 47 | session: *const c_void 48 | } 49 | impl Drop for AuthorizedSession { 50 | fn drop(&mut self) { 51 | unsafe { 52 | magic::free_override_session(self.session); 53 | } 54 | } 55 | } 56 | 57 | pub fn authorize_override() -> Result { 58 | let session = unsafe { 59 | magic::issue_override_code(OverrideCode::IssueOverride as i32); 60 | magic::poll_override_session() 61 | }; 62 | let session = AuthorizedSession { 63 | session: session 64 | }; 65 | check_error(session) 66 | } 67 | 68 | pub fn authorize_privileged() -> Result { 69 | let session = unsafe { 70 | magic::issue_override_code(OverrideCode::IssuePrivileged as i32); 71 | magic::poll_physical_override_privileged_session() 72 | }; 73 | let session = AuthorizedSession { 74 | session: session 75 | }; 76 | check_error(session) 77 | } 78 | 79 | pub fn authorize_admin() -> Result { 80 | let session = unsafe { 81 | magic::issue_override_code(OverrideCode::IssueAdmin as i32); 82 | magic::poll_physical_override_admin_session() 83 | }; 84 | let session = AuthorizedSession { 85 | session: session 86 | }; 87 | check_error(session) 88 | } 89 | 90 | pub fn reset_state() 91 | { 92 | unsafe { 93 | magic::override_reset_state(); 94 | } 95 | } 96 | 97 | pub fn check_error(t: T) -> Result 98 | { 99 | let err = unsafe { 100 | magic::poll_override_error() 101 | }; 102 | if err==0 { 103 | Result::Ok(t) 104 | } else { 105 | Result::Err(toErrorCode(err)) 106 | } 107 | } 108 | 109 | pub fn input_floor(floor: i32) -> Result<(),ErrorCode> 110 | { 111 | unsafe { 112 | magic::override_input_floor(floor); 113 | } 114 | check_error(()) 115 | } 116 | 117 | pub fn manual_mode() -> Result<(),ErrorCode> 118 | { 119 | unsafe { 120 | magic::override_manual_mode(); 121 | } 122 | check_error(()) 123 | } 124 | 125 | pub fn normal_mode() -> Result<(),ErrorCode> 126 | { 127 | unsafe { 128 | magic::override_normal_mode(); 129 | } 130 | check_error(()) 131 | } 132 | 133 | pub fn flash(pattern: i32) -> Result<(),ErrorCode> 134 | { 135 | unsafe { 136 | magic::elevator_display_flash(pattern); 137 | } 138 | check_error(()) 139 | } 140 | pub fn toggle_light(light_id: i32) -> Result<(),ErrorCode> 141 | { 142 | unsafe { 143 | magic::elevator_display_toggle_light(light_id); 144 | } 145 | check_error(()) 146 | } 147 | pub fn set_light_color(light_id: i32, color: i32) -> Result<(),ErrorCode> 148 | { 149 | unsafe { 150 | magic::elevator_display_set_light_color(light_id, color); 151 | } 152 | check_error(()) 153 | } 154 | 155 | pub fn is_override() -> bool 156 | { 157 | unsafe { 158 | magic::is_override() != 0 159 | } 160 | } 161 | 162 | pub fn is_privileged() -> bool 163 | { 164 | unsafe { 165 | magic::is_privileged() != 0 166 | } 167 | } 168 | 169 | pub fn is_admin() -> bool 170 | { 171 | unsafe { 172 | magic::is_admin() != 0 173 | } 174 | } 175 | 176 | -------------------------------------------------------------------------------- /Chapter06/src/elevator_magic.c: -------------------------------------------------------------------------------- 1 | int input_codes[3] = {0, 0, 0}; 2 | int error_code = 0; 3 | void *active_session = 0; 4 | void *poll_session = 0; 5 | int active_light = 0; 6 | int active_light_toggle = 0; 7 | 8 | int is_override() { 9 | return (active_session == ((void*)1)); 10 | } 11 | 12 | int is_privileged() { 13 | return (active_session == ((void*)2)); 14 | } 15 | 16 | int is_admin() { 17 | return (active_session == ((void*)3)); 18 | } 19 | 20 | void issue_override_code(int code) { 21 | input_codes[0] = 0; 22 | input_codes[1] = 0; 23 | input_codes[2] = 0; 24 | input_codes[0] = code; 25 | if( code == 1 ) { 26 | if( active_session == ((void*)0) ) { 27 | active_session = (void*)1; 28 | poll_session = (void*)1; 29 | } else { 30 | active_session = (void*)4; 31 | poll_session = (void*)0; 32 | error_code = 1; 33 | } 34 | } else if( code == 2 ) { 35 | if( active_session == ((void*)0) ) { 36 | active_session = (void*)2; 37 | poll_session = (void*)2; 38 | } else { 39 | active_session = (void*)4; 40 | poll_session = (void*)0; 41 | error_code = 1; 42 | } 43 | } else if( code == 3 ) { 44 | if( active_session == ((void*)0) ) { 45 | active_session = (void*)3; 46 | poll_session = (void*)3; 47 | } else { 48 | active_session = (void*)4; 49 | poll_session = (void*)0; 50 | error_code = 1; 51 | } 52 | } 53 | } 54 | 55 | int poll_override_code() { 56 | int code = input_codes[0]; 57 | input_codes[0] = input_codes[1]; 58 | input_codes[1] = input_codes[2]; 59 | input_codes[2] = 0; 60 | return code; 61 | } 62 | 63 | int poll_override_input_floor() { 64 | //C Code 65 | return 0; 66 | } 67 | 68 | int poll_override_error() { 69 | return error_code; 70 | } 71 | 72 | void* poll_override_session() { 73 | return poll_session; 74 | } 75 | 76 | void free_override_session(void* session) { 77 | if( active_session == 0 ) { 78 | error_code = 2; 79 | } 80 | active_session = 0; 81 | } 82 | 83 | void* poll_physical_override_privileged_session() { 84 | return poll_session; 85 | } 86 | 87 | void* poll_physical_override_admin_session() { 88 | return poll_session; 89 | } 90 | 91 | void override_input_floor(int floor) { 92 | if(active_session) { 93 | input_codes[0] = 4; 94 | input_codes[1] = floor; 95 | input_codes[2] = 0; 96 | } else { 97 | error_code = 3; 98 | } 99 | } 100 | 101 | void override_manual_mode() { 102 | if(active_session) { 103 | input_codes[0] = 5; 104 | input_codes[1] = 0; 105 | input_codes[2] = 0; 106 | } else { 107 | error_code = 3; 108 | } 109 | } 110 | 111 | void override_normal_mode() { 112 | if(active_session) { 113 | input_codes[0] = 6; 114 | input_codes[1] = 0; 115 | input_codes[2] = 0; 116 | } else { 117 | error_code = 3; 118 | } 119 | } 120 | 121 | void override_reset_state() { 122 | input_codes[0] = 0; 123 | input_codes[1] = 0; 124 | input_codes[2] = 0; 125 | error_code = 0; 126 | active_session = 0; 127 | poll_session = 0; 128 | active_light = 0; 129 | active_light_toggle = 0; 130 | } 131 | 132 | void elevator_display_flash(int pattern) { 133 | input_codes[0] = 7; 134 | input_codes[1] = pattern; 135 | input_codes[2] = 0; 136 | } 137 | 138 | void elevator_display_toggle_light(int light_id) { 139 | int last_state = 0; 140 | if( active_light == light_id ) { 141 | last_state = active_light_toggle; 142 | } 143 | active_light = light_id; 144 | active_light_toggle = last_state ? 0 : 1; 145 | input_codes[0] = 8; 146 | input_codes[1] = light_id; 147 | input_codes[2] = active_light_toggle; 148 | } 149 | 150 | void elevator_display_set_light_color(int light_id, int color) { 151 | input_codes[0] = 9; 152 | input_codes[1] = light_id; 153 | input_codes[2] = color; 154 | } 155 | -------------------------------------------------------------------------------- /Chapter06/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | extern crate libc; 3 | 4 | mod magic; 5 | mod admin; 6 | mod tests; 7 | -------------------------------------------------------------------------------- /Chapter06/src/magic.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_int, c_void}; 2 | 3 | #[link(name = "elevatormagic")] 4 | extern { 5 | pub fn issue_override_code(code: c_int); 6 | pub fn poll_override_code() -> c_int; 7 | pub fn poll_override_input_floor() -> c_int; 8 | pub fn poll_override_error() -> c_int; 9 | pub fn poll_override_session() -> *const c_void; 10 | pub fn free_override_session(session: *const c_void); 11 | pub fn poll_physical_override_privileged_session() -> *const c_void; 12 | pub fn poll_physical_override_admin_session() -> *const c_void; 13 | pub fn override_input_floor(floor: c_int); 14 | pub fn override_manual_mode(); 15 | pub fn override_normal_mode(); 16 | pub fn override_reset_state(); 17 | pub fn elevator_display_flash(pattern: c_int); 18 | pub fn elevator_display_toggle_light(light_id: c_int); 19 | pub fn elevator_display_set_light_color(light_id: c_int, color: c_int); 20 | pub fn is_override() -> c_int; 21 | pub fn is_privileged() -> c_int; 22 | pub fn is_admin() -> c_int; 23 | } 24 | -------------------------------------------------------------------------------- /Chapter06/src/tests/admin.rs: -------------------------------------------------------------------------------- 1 | use admin; 2 | 3 | #[test] 4 | fn authorize_override() { 5 | admin::reset_state(); 6 | { 7 | let session = admin::authorize_override().ok(); 8 | assert!(admin::is_override()); 9 | } 10 | assert!(!admin::is_override()); 11 | assert!(admin::check_error(()).is_ok()); 12 | } 13 | #[test] 14 | fn authorize_privileged() { 15 | admin::reset_state(); 16 | { 17 | let session = admin::authorize_privileged().ok(); 18 | assert!(admin::is_privileged()); 19 | } 20 | assert!(!admin::is_privileged()); 21 | assert!(admin::check_error(()).is_ok()); 22 | } 23 | #[test] 24 | fn issue_admin_code() { 25 | admin::reset_state(); 26 | { 27 | let session = admin::authorize_admin().ok(); 28 | assert!(admin::is_admin()); 29 | } 30 | assert!(!admin::is_admin()); 31 | assert!(admin::check_error(()).is_ok()); 32 | } 33 | 34 | #[test] 35 | fn double_override_failure() { 36 | admin::reset_state(); 37 | let session = admin::authorize_override().ok(); 38 | assert!(admin::authorize_override().err().is_some()); 39 | assert!(!admin::check_error(()).is_ok()); 40 | } 41 | #[test] 42 | fn double_privileged_failure() { 43 | admin::reset_state(); 44 | let session = admin::authorize_privileged().ok(); 45 | assert!(admin::authorize_privileged().err().is_some()); 46 | assert!(!admin::check_error(()).is_ok()); 47 | } 48 | #[test] 49 | fn double_admin_failure() { 50 | admin::reset_state(); 51 | let session = admin::authorize_admin().ok(); 52 | assert!(admin::authorize_admin().err().is_some()); 53 | assert!(!admin::check_error(()).is_ok()); 54 | } 55 | 56 | #[test] 57 | fn clone_override() { 58 | admin::reset_state(); 59 | { 60 | let session = admin::authorize_override().ok().unwrap(); 61 | let session2 = session.clone(); 62 | assert!(admin::is_override()); 63 | } 64 | assert!(!admin::is_override()); 65 | assert!(admin::check_error(()).is_ok()); 66 | } 67 | #[test] 68 | fn clone_privileged() { 69 | admin::reset_state(); 70 | { 71 | let session = admin::authorize_privileged().ok().unwrap(); 72 | let session2 = session.clone(); 73 | assert!(admin::is_privileged()); 74 | } 75 | assert!(!admin::is_privileged()); 76 | assert!(admin::check_error(()).is_ok()); 77 | } 78 | #[test] 79 | fn clone_admin() { 80 | admin::reset_state(); 81 | { 82 | let session = admin::authorize_admin().ok().unwrap(); 83 | let session2 = session.clone(); 84 | assert!(admin::is_admin()); 85 | } 86 | assert!(!admin::is_admin()); 87 | assert!(admin::check_error(()).is_ok()); 88 | } 89 | 90 | #[test] 91 | fn input_floor() { 92 | admin::reset_state(); 93 | { 94 | let session = admin::authorize_admin().ok(); 95 | admin::input_floor(2).ok(); 96 | } 97 | assert!(!admin::is_admin()); 98 | assert!(admin::check_error(()).is_ok()); 99 | } 100 | 101 | #[test] 102 | fn manual_mode() { 103 | admin::reset_state(); 104 | { 105 | let session = admin::authorize_admin().ok(); 106 | admin::manual_mode().ok(); 107 | } 108 | assert!(!admin::is_admin()); 109 | assert!(admin::check_error(()).is_ok()); 110 | } 111 | 112 | #[test] 113 | fn normal_mode() { 114 | admin::reset_state(); 115 | { 116 | let session = admin::authorize_admin().ok(); 117 | admin::normal_mode().ok(); 118 | } 119 | assert!(!admin::is_admin()); 120 | assert!(admin::check_error(()).is_ok()); 121 | } 122 | 123 | #[test] 124 | fn flash() { 125 | admin::reset_state(); 126 | assert!(!admin::is_override()); 127 | assert!(!admin::is_privileged()); 128 | assert!(!admin::is_admin()); 129 | admin::flash(222).ok(); 130 | assert!(admin::check_error(()).is_ok()); 131 | } 132 | 133 | #[test] 134 | fn toggle_light() { 135 | admin::reset_state(); 136 | assert!(!admin::is_override()); 137 | assert!(!admin::is_privileged()); 138 | assert!(!admin::is_admin()); 139 | admin::toggle_light(7).ok(); 140 | assert!(admin::check_error(()).is_ok()); 141 | } 142 | 143 | #[test] 144 | fn set_light_color() { 145 | admin::reset_state(); 146 | assert!(!admin::is_override()); 147 | assert!(!admin::is_privileged()); 148 | assert!(!admin::is_admin()); 149 | admin::set_light_color(33, 123).ok(); 150 | assert!(admin::check_error(()).is_ok()); 151 | } 152 | 153 | #[test] 154 | fn deny_input_floor() { 155 | admin::reset_state(); 156 | admin::input_floor(2).err(); 157 | assert!(!admin::check_error(()).is_ok()); 158 | } 159 | 160 | #[test] 161 | fn deny_manual_mode() { 162 | admin::reset_state(); 163 | admin::manual_mode().err(); 164 | assert!(!admin::check_error(()).is_ok()); 165 | } 166 | 167 | #[test] 168 | fn deny_normal_mode() { 169 | admin::reset_state(); 170 | admin::normal_mode().err(); 171 | assert!(!admin::check_error(()).is_ok()); 172 | } 173 | -------------------------------------------------------------------------------- /Chapter06/src/tests/magic.rs: -------------------------------------------------------------------------------- 1 | use magic; 2 | use libc::{c_void}; 3 | 4 | #[test] 5 | fn issue_override_code() { 6 | unsafe { 7 | magic::override_reset_state(); 8 | magic::issue_override_code(1); 9 | assert!(magic::poll_override_code() == 1); 10 | assert!(magic::poll_override_error() == 0); 11 | } 12 | } 13 | #[test] 14 | fn issue_privileged_code() { 15 | unsafe { 16 | magic::override_reset_state(); 17 | magic::issue_override_code(2); 18 | assert!(magic::poll_override_code() == 2); 19 | assert!(magic::poll_override_error() == 0); 20 | } 21 | } 22 | #[test] 23 | fn issue_admin_code() { 24 | unsafe { 25 | magic::override_reset_state(); 26 | magic::issue_override_code(3); 27 | assert!(magic::poll_override_code() == 3); 28 | assert!(magic::poll_override_error() == 0); 29 | } 30 | } 31 | 32 | #[test] 33 | fn authorize_override_success() { 34 | unsafe { 35 | magic::override_reset_state(); 36 | magic::issue_override_code(1); 37 | let session = magic::poll_override_session(); 38 | assert!(session != (0 as *const c_void)); 39 | magic::free_override_session(session); 40 | assert!(magic::poll_override_error() == 0); 41 | } 42 | } 43 | #[test] 44 | fn authorize_privileged_success() { 45 | unsafe { 46 | magic::override_reset_state(); 47 | magic::issue_override_code(2); 48 | let session = magic::poll_physical_override_privileged_session(); 49 | assert!(session != (0 as *const c_void)); 50 | magic::free_override_session(session); 51 | assert!(magic::poll_override_error() == 0); 52 | } 53 | } 54 | #[test] 55 | fn authorize_admin_success() { 56 | unsafe { 57 | magic::override_reset_state(); 58 | magic::issue_override_code(3); 59 | let session = magic::poll_physical_override_admin_session(); 60 | assert!(session != (0 as *const c_void)); 61 | magic::free_override_session(session); 62 | assert!(magic::poll_override_error() == 0); 63 | } 64 | } 65 | 66 | #[test] 67 | fn double_override_failure() { 68 | unsafe { 69 | magic::override_reset_state(); 70 | magic::issue_override_code(1); 71 | magic::issue_override_code(1); 72 | assert!(magic::poll_override_session() == (0 as *const c_void)); 73 | assert!(magic::poll_override_error() == 1); 74 | } 75 | } 76 | #[test] 77 | fn double_privileged_failure() { 78 | unsafe { 79 | magic::override_reset_state(); 80 | magic::issue_override_code(2); 81 | magic::issue_override_code(2); 82 | assert!(magic::poll_physical_override_privileged_session() == (0 as *const c_void)); 83 | assert!(magic::poll_override_error() == 1); 84 | } 85 | } 86 | #[test] 87 | fn double_admin_failure() { 88 | unsafe { 89 | magic::override_reset_state(); 90 | magic::issue_override_code(3); 91 | magic::issue_override_code(3); 92 | assert!(magic::poll_physical_override_admin_session() == (0 as *const c_void)); 93 | assert!(magic::poll_override_error() == 1); 94 | } 95 | } 96 | 97 | #[test] 98 | fn double_free_override_failure() { 99 | unsafe { 100 | magic::override_reset_state(); 101 | magic::issue_override_code(1); 102 | let session = magic::poll_override_session(); 103 | assert!(session != (0 as *const c_void)); 104 | magic::free_override_session(session); 105 | magic::free_override_session(session); 106 | assert!(magic::poll_override_error() == 2); 107 | } 108 | } 109 | #[test] 110 | fn double_free_privileged_failure() { 111 | unsafe { 112 | magic::override_reset_state(); 113 | magic::issue_override_code(2); 114 | let session = magic::poll_physical_override_privileged_session(); 115 | assert!(session != (0 as *const c_void)); 116 | magic::free_override_session(session); 117 | magic::free_override_session(session); 118 | assert!(magic::poll_override_error() == 2); 119 | } 120 | } 121 | #[test] 122 | fn double_free_admin_failure() { 123 | unsafe { 124 | magic::override_reset_state(); 125 | magic::issue_override_code(3); 126 | let session = magic::poll_physical_override_admin_session(); 127 | assert!(session != (0 as *const c_void)); 128 | magic::free_override_session(session); 129 | magic::free_override_session(session); 130 | assert!(magic::poll_override_error() == 2); 131 | } 132 | } 133 | 134 | #[test] 135 | fn input_floor() { 136 | unsafe { 137 | magic::override_reset_state(); 138 | magic::issue_override_code(3); 139 | magic::override_input_floor(2); 140 | assert!(magic::poll_override_code() == 4); 141 | assert!(magic::poll_override_code() == 2); 142 | assert!(magic::poll_override_error() == 0); 143 | } 144 | } 145 | 146 | #[test] 147 | fn manual_mode() { 148 | unsafe { 149 | magic::override_reset_state(); 150 | magic::issue_override_code(3); 151 | magic::override_manual_mode(); 152 | assert!(magic::poll_override_code() == 5); 153 | assert!(magic::poll_override_error() == 0); 154 | } 155 | } 156 | 157 | #[test] 158 | fn normal_mode() { 159 | unsafe { 160 | magic::override_reset_state(); 161 | magic::issue_override_code(3); 162 | magic::override_normal_mode(); 163 | assert!(magic::poll_override_code() == 6); 164 | assert!(magic::poll_override_error() == 0); 165 | } 166 | } 167 | 168 | #[test] 169 | fn flash() { 170 | unsafe { 171 | magic::override_reset_state(); 172 | magic::elevator_display_flash(222); 173 | assert!(magic::poll_override_code() == 7); 174 | assert!(magic::poll_override_code() == 222); 175 | } 176 | } 177 | 178 | #[test] 179 | fn toggle_light() { 180 | unsafe { 181 | magic::override_reset_state(); 182 | magic::elevator_display_toggle_light(33); 183 | assert!(magic::poll_override_code() == 8); 184 | assert!(magic::poll_override_code() == 33); 185 | assert!(magic::poll_override_code() == 1); 186 | magic::elevator_display_toggle_light(33); 187 | assert!(magic::poll_override_code() == 8); 188 | assert!(magic::poll_override_code() == 33); 189 | assert!(magic::poll_override_code() == 0); 190 | } 191 | } 192 | 193 | #[test] 194 | fn set_light_color() { 195 | unsafe { 196 | magic::override_reset_state(); 197 | magic::elevator_display_set_light_color(33, 222); 198 | assert!(magic::poll_override_code() == 9); 199 | assert!(magic::poll_override_code() == 33); 200 | assert!(magic::poll_override_code() == 222); 201 | } 202 | } 203 | 204 | #[test] 205 | fn deny_input_floor() { 206 | unsafe { 207 | magic::override_reset_state(); 208 | magic::override_input_floor(2); 209 | assert!(magic::poll_override_error() == 3); 210 | } 211 | } 212 | 213 | #[test] 214 | fn deny_manual_mode() { 215 | unsafe { 216 | magic::override_reset_state(); 217 | magic::override_manual_mode(); 218 | assert!(magic::poll_override_error() == 3); 219 | } 220 | } 221 | 222 | #[test] 223 | fn deny_normal_mode() { 224 | unsafe { 225 | magic::override_reset_state(); 226 | magic::override_manual_mode(); 227 | assert!(magic::poll_override_error() == 3); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /Chapter06/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | mod magic; 3 | mod admin; 4 | -------------------------------------------------------------------------------- /Chapter07/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "Chapter7" 3 | version = "1.0.0" 4 | 5 | [dependencies] 6 | chomp = "0.3.1" 7 | 8 | [[bin]] 9 | name = "functor_pattern" 10 | path = "functor_pattern.rs" 11 | 12 | [[bin]] 13 | name = "monad_pattern" 14 | path = "monad_pattern.rs" 15 | 16 | [[bin]] 17 | name = "combinator_pattern" 18 | path = "combinator_pattern.rs" 19 | 20 | [[bin]] 21 | name = "lazy_pattern" 22 | path = "lazy_pattern.rs" 23 | -------------------------------------------------------------------------------- /Chapter07/combinator_pattern.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate chomp; 3 | use chomp::prelude::*; 4 | use std::rc::Rc; 5 | 6 | #[derive(Debug, Eq, PartialEq)] 7 | struct Name { 8 | first: B, 9 | last: B, 10 | } 11 | 12 | fn name(i: I) -> SimpleResult> { 13 | parse!{i; 14 | let first = take_while1(|c| c != b' '); 15 | token(b' '); // skipping this char 16 | let last = take_while1(|c| c != b'\n'); 17 | 18 | ret Name{ 19 | first: first, 20 | last: last, 21 | } 22 | } 23 | } 24 | 25 | 26 | #[derive(Clone)] 27 | struct ParseState { 28 | buffer: Rc>, 29 | index: usize, 30 | a: A 31 | } 32 | impl ParseState { 33 | fn new(a: A, buffer: String) -> ParseState { 34 | let buffer: Vec = buffer.chars().collect(); 35 | ParseState { 36 | buffer: Rc::new(buffer), 37 | index: 0, 38 | a: a 39 | } 40 | } 41 | fn next(&self) -> (ParseState,Option) { 42 | if self.index < self.buffer.len() { 43 | let new_char = self.buffer[self.index]; 44 | let new_index = self.index + 1; 45 | (ParseState { 46 | buffer: Rc::clone(&self.buffer), 47 | index: new_index, 48 | a: self.a.clone() 49 | }, Some(new_char)) 50 | } else { 51 | (ParseState { 52 | buffer: Rc::clone(&self.buffer), 53 | index: self.index, 54 | a: self.a.clone() 55 | },None) 56 | } 57 | } 58 | } 59 | 60 | #[derive(Debug)] 61 | struct ParseRCon(A,Result,String>); 62 | 63 | #[derive(Debug)] 64 | enum ParseOutput { 65 | Success(A), 66 | Failure(String) 67 | } 68 | 69 | fn parse(p: &P, st: &ParseState) -> ParseOutput 70 | where P: Fn(ParseState) -> ParseRCon,A> { 71 | match p(st.clone()) { 72 | ParseRCon(_,Ok(Some(a))) => ParseOutput::Success(a), 73 | ParseRCon(_,Ok(None)) => ParseOutput::Failure("expected input".to_string()), 74 | ParseRCon(_,Err(err)) => ParseOutput::Failure(err) 75 | } 76 | } 77 | 78 | fn parse_mzero(st: ParseState) -> ParseRCon,A> { 79 | ParseRCon(st,Err("mzero failed".to_string())) 80 | } 81 | 82 | fn parse_return(a: A) -> impl (Fn(ParseState) -> ParseRCon,A>) { 83 | move |st| { ParseRCon(st,Ok(Some(a.clone()))) } 84 | } 85 | 86 | fn parse_token(t: T) -> impl (Fn(ParseState) -> ParseRCon,A>) 87 | where T: 'static + Fn(char) -> Option { 88 | move |st: ParseState| { 89 | let (next_state,next_char) = st.clone().next(); 90 | match next_char { 91 | Some(c) => ParseRCon(next_state,Ok(t(c))), 92 | None => ParseRCon(st,Err("end of input".to_string())) 93 | } 94 | } 95 | } 96 | 97 | fn parse_satisfy(t: T) -> impl (Fn(ParseState) -> ParseRCon,char>) 98 | where T: 'static + Fn(char) -> bool { 99 | parse_token(move |c| if t(c) {Some(c)} else {None}) 100 | } 101 | 102 | fn parse_bind(p1: P1, b1: B1) 103 | -> impl Fn(ParseState) -> ParseRCon,B> 104 | where P1: Fn(ParseState) -> ParseRCon,A>, 105 | P2: Fn(ParseState) -> ParseRCon,B>, 106 | B1: Fn(A) -> P2 { 107 | move |st| { 108 | match p1(st) { 109 | ParseRCon(nst,Ok(Some(a))) => b1(a)(nst), 110 | ParseRCon(nst,Ok(None)) => ParseRCon(nst,Err("bind failed".to_string())), 111 | ParseRCon(nst,Err(err)) => ParseRCon(nst,Err(err)) 112 | } 113 | } 114 | } 115 | 116 | fn parse_sequence(p1: P1, p2: P2) 117 | -> impl Fn(ParseState) -> ParseRCon,B> 118 | where P1: Fn(ParseState) -> ParseRCon,A>, 119 | P2: Fn(ParseState) -> ParseRCon,B> { 120 | move |st| { 121 | match p1(st) { 122 | ParseRCon(nst,Ok(_)) => p2(nst), 123 | ParseRCon(nst,Err(err)) => ParseRCon(nst,Err(err)) 124 | } 125 | } 126 | } 127 | 128 | fn parse_or(p1: P1, p2: P2) 129 | -> impl Fn(ParseState) -> ParseRCon,A> 130 | where P1: Fn(ParseState) -> ParseRCon,A>, 131 | P2: Fn(ParseState) -> ParseRCon,A> { 132 | move |st| { 133 | match p1(st.clone()) { 134 | ParseRCon(nst,Ok(Some(a))) => ParseRCon(nst,Ok(Some(a))), 135 | ParseRCon(_,Ok(None)) => p2(st), 136 | ParseRCon(nst,Err(err)) => ParseRCon(nst,Err(err)) 137 | } 138 | } 139 | } 140 | 141 | fn compose(f: F, g: G) -> impl Fn(A) -> C 142 | where F: 'static + Fn(A) -> B, 143 | G: 'static + Fn(B) -> C { 144 | move |x| g(f(x)) 145 | } 146 | 147 | fn main() 148 | { 149 | let fa = |x| x+1; 150 | let fb = |y| y*2; 151 | let fc = |z| z/3; 152 | 153 | let g = compose(compose(fa,fb),fc); 154 | println!("g(1) = {}", g(1)); 155 | println!("g(12) = {}", g(12)); 156 | println!("g(123) = {}", g(123)); 157 | 158 | let parse_result = parse_only(name, "Martin Wernstål\n".as_bytes()).unwrap(); 159 | println!("first:{} last:{}", 160 | String::from_utf8_lossy(parse_result.first), 161 | String::from_utf8_lossy(parse_result.last)); 162 | 163 | let input1 = ParseState::new((), "1 + 2 * 3".to_string()); 164 | let input2 = ParseState::new((), "3 / 2 - 1".to_string()); 165 | 166 | let p1 = parse_mzero::<(),()>; 167 | println!("p1 input1: {:?}", parse(&p1,&input1)); 168 | println!("p1 input2: {:?}", parse(&p1,&input2)); 169 | 170 | let p2 = parse_return(123); 171 | println!("p2 input1: {:?}", parse(&p2,&input1)); 172 | println!("p2 input2: {:?}", parse(&p2,&input2)); 173 | 174 | let p3 = parse_satisfy(|c| c=='1'); 175 | println!("p3 input1: {:?}", parse(&p3,&input1)); 176 | println!("p3 input2: {:?}", parse(&p3,&input2)); 177 | 178 | let digit = parse_satisfy(|c| c.is_digit(10)); 179 | println!("digit input1: {:?}", parse(&digit,&input1)); 180 | println!("digit input2: {:?}", parse(&digit,&input2)); 181 | 182 | let space = parse_satisfy(|c| c==' '); 183 | println!("space input1: {:?}", parse(&space,&input1)); 184 | println!("space input2: {:?}", parse(&space,&input2)); 185 | 186 | let operator = parse_satisfy(|c| c=='+' || c=='-' || c=='*' || c=='/'); 187 | println!("operator input1: {:?}", parse(&operator,&input1)); 188 | println!("operator input2: {:?}", parse(&operator,&input2)); 189 | 190 | let ps1 = parse_sequence(digit,space); 191 | let ps2 = parse_sequence(ps1,operator); 192 | println!("digit,space,operator input1: {:?}", parse(&ps2,&input1)); 193 | println!("digit,space,operator input2: {:?}", parse(&ps2,&input2)); 194 | } 195 | -------------------------------------------------------------------------------- /Chapter07/functor_pattern.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashSet}; 2 | 3 | struct WebCamera; 4 | #[derive(Debug)] 5 | enum VisibleEmotion { 6 | Anger, 7 | Contempt, 8 | Disgust, 9 | Fear, 10 | Happiness, 11 | Neutral, 12 | Sadness, 13 | Surprise 14 | } 15 | #[derive(Debug,Clone)] 16 | struct BoundingBox { 17 | top: u64, 18 | left: u64, 19 | height: u64, 20 | width: u64 21 | } 22 | #[derive(Debug)] 23 | enum CameraFilters { 24 | Sparkles, 25 | Rain, 26 | Fire, 27 | Disco 28 | } 29 | 30 | impl WebCamera { 31 | fn map_emotion(&self, translate: F) -> Vec<(BoundingBox,T)> 32 | where F: Fn(VisibleEmotion) -> T { 33 | //Simulate emotion extracted from WebCamera 34 | vec![ 35 | (BoundingBox { top: 1, left: 1, height: 1, width: 1 }, VisibleEmotion::Anger), 36 | (BoundingBox { top: 1, left: 1, height: 1, width: 1 }, VisibleEmotion::Sadness), 37 | (BoundingBox { top: 4, left: 4, height: 1, width: 1 }, VisibleEmotion::Surprise), 38 | (BoundingBox { top: 8, left: 1, height: 1, width: 1 }, VisibleEmotion::Neutral) 39 | ].into_iter().map(|(bb,emt)| { 40 | (bb, translate(emt)) 41 | }).collect::>() 42 | } 43 | fn flatmap_emotion>(&self, mut translate: F) -> Vec<(BoundingBox,T)> 44 | where F: FnMut(VisibleEmotion) -> U { 45 | //Simulate emotion extracted from WebCamera 46 | vec![ 47 | (BoundingBox { top: 1, left: 1, height: 1, width: 1 }, VisibleEmotion::Anger), 48 | (BoundingBox { top: 1, left: 1, height: 1, width: 1 }, VisibleEmotion::Sadness), 49 | (BoundingBox { top: 4, left: 4, height: 1, width: 1 }, VisibleEmotion::Surprise), 50 | (BoundingBox { top: 8, left: 1, height: 1, width: 1 }, VisibleEmotion::Neutral) 51 | ].into_iter().flat_map(|(bb,emt)| { 52 | translate(emt).into_iter().map(move |t| (bb.clone(), t)) 53 | }).collect::>() 54 | } 55 | } 56 | 57 | fn main() 58 | { 59 | let m: Vec = vec![1, 2, 3]; 60 | let n: Vec = m.iter().map(|x| { x*x }).collect(); 61 | println!("{:?}", m); 62 | println!("{:?}", n); 63 | 64 | let mut a: HashSet = HashSet::new(); 65 | a.insert(1); 66 | a.insert(2); 67 | a.insert(3); 68 | a.insert(4); 69 | let b: HashSet = a.iter().cloned().map(|x| x/2).collect(); 70 | println!("{:?}", a); 71 | println!("{:?}", b); 72 | 73 | let sentences = vec!["this is a sentence","paragraphs have many sentences"]; 74 | let words:Vec<&str> = sentences.iter().flat_map(|&x| x.split(" ")).collect(); 75 | println!("{:?}", sentences); 76 | println!("{:?}", words); 77 | 78 | let v: Vec = vec![1, 2, 3]; 79 | let s: HashSet = v.iter().cloned().map(|x| x/2).collect(); 80 | println!("{:?}", v); 81 | println!("{:?}", s); 82 | 83 | let camera = WebCamera; 84 | let emotes: Vec<(BoundingBox,VisibleEmotion)> = camera.map_emotion(|emt| { 85 | match emt { 86 | VisibleEmotion::Anger | 87 | VisibleEmotion::Contempt | 88 | VisibleEmotion::Disgust | 89 | VisibleEmotion::Fear | 90 | VisibleEmotion::Sadness => VisibleEmotion::Happiness, 91 | VisibleEmotion::Neutral | 92 | VisibleEmotion::Happiness | 93 | VisibleEmotion::Surprise => VisibleEmotion::Sadness 94 | } 95 | }); 96 | let filters: Vec<(BoundingBox,CameraFilters)> = camera.flatmap_emotion(|emt| { 97 | match emt { 98 | VisibleEmotion::Anger | 99 | VisibleEmotion::Contempt | 100 | VisibleEmotion::Disgust | 101 | VisibleEmotion::Fear | 102 | VisibleEmotion::Sadness => vec![CameraFilters::Sparkles, CameraFilters::Rain], 103 | VisibleEmotion::Neutral | 104 | VisibleEmotion::Happiness | 105 | VisibleEmotion::Surprise => vec![CameraFilters::Disco] 106 | } 107 | }); 108 | println!("{:?}",emotes); 109 | println!("{:?}",filters); 110 | } 111 | -------------------------------------------------------------------------------- /Chapter07/monad_pattern.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug}; 2 | use std::io::prelude::*; 3 | use std::net::TcpListener; 4 | use std::net::TcpStream; 5 | use std::thread; 6 | 7 | struct ServerMonad { 8 | state: St, 9 | handlers: Vec Option>> 10 | } 11 | impl ServerMonad { 12 | fn _return(st: St) -> ServerMonad { 13 | ServerMonad { 14 | state: st, 15 | handlers: Vec::new() 16 | } 17 | } 18 | fn listen(&mut self, address: &str) { 19 | let listener = TcpListener::bind(address).unwrap(); 20 | for stream in listener.incoming() { 21 | let mut st = self.state.clone(); 22 | let mut buffer = [0; 2048]; 23 | let mut tcp = stream.unwrap(); 24 | tcp.read(&mut buffer); 25 | let buffer = String::from_utf8_lossy(&buffer).into_owned(); 26 | for h in self.handlers.iter() { 27 | if let Some(response) = h(&mut st,&buffer) { 28 | tcp.write(response.as_bytes()); 29 | break 30 | } 31 | } 32 | } 33 | } 34 | fn bind_handler(mut self, f: F) -> Self 35 | where F: 'static + Fn(&mut St,&String) -> Option { 36 | self.handlers.push(Box::new(f)); 37 | self 38 | } 39 | } 40 | 41 | struct LogMonad(T); 42 | impl LogMonad { 43 | fn _return(t: T) -> LogMonad 44 | where T: Debug { 45 | println!("{:?}", t); 46 | LogMonad(t) 47 | } 48 | fn bind(&self, f: F) -> LogMonad 49 | where F: Fn(&T) -> R, 50 | R: Debug { 51 | let r = f(&self.0); 52 | println!("{:?}", r); 53 | LogMonad(r) 54 | } 55 | } 56 | 57 | struct LazyMonad(Box B>); 58 | impl LazyMonad { 59 | fn _return(u: A) -> LazyMonad { 60 | LazyMonad(Box::new(move |b: B| b)) 61 | } 62 | fn bind(self, g: G) -> LazyMonad 63 | where G: Fn(B) -> C { 64 | LazyMonad(Box::new(move |a: A| g(self.0(a)))) 65 | } 66 | fn apply(self, a: A) -> B { 67 | self.0(a) 68 | } 69 | } 70 | 71 | fn main() 72 | { 73 | let v1 = Some(2).and_then(|x| Some(x+x)).and_then(|y| Some(y*y)); 74 | println!("{:?}", v1); 75 | 76 | let v2 = None.or_else(|| None).or_else(|| Some(222)); 77 | println!("{:?}", v2); 78 | 79 | let v3 = Some(2).and_then(|x| Some("abc")); 80 | println!("{:?}", v3); 81 | 82 | // or_else is not quite a monad 83 | // does not permit polymorphic bind 84 | //let v4 = Some(2).or_else(|| Some("abc")); 85 | //println!("{:?}", v4); 86 | 87 | LogMonad::_return(4) 88 | .bind(|x| x+x) 89 | .bind(|y| y*y) 90 | .bind(|z| format!("{}{}{}", z, z, z)); 91 | 92 | let notyet = LazyMonad::_return(()) 93 | .bind(|x| x+2) 94 | .bind(|y| y*3) 95 | .bind(|z| format!("{}{}", z, z)); 96 | 97 | let nowdoit = notyet.apply(222); 98 | println!("nowdoit {}", nowdoit); 99 | 100 | ServerMonad::_return(()) 101 | .bind_handler(|&mut st, ref msg| if msg.len()%2 == 0 { Some("divisible by 2".to_string()) } else { None }) 102 | .bind_handler(|&mut st, ref msg| if msg.len()%3 == 0 { Some("divisible by 3".to_string()) } else { None }) 103 | .bind_handler(|&mut st, ref msg| if msg.len()%5 == 0 { Some("divisible by 5".to_string()) } else { None }) 104 | .bind_handler(|&mut st, ref msg| if msg.len()%7 == 0 { Some("divisible by 7".to_string()) } else { None }) 105 | .listen("127.0.0.1:8888"); 106 | } 107 | -------------------------------------------------------------------------------- /Chapter08/.gitignore: -------------------------------------------------------------------------------- 1 | process_a 2 | process_b 3 | -------------------------------------------------------------------------------- /Chapter08/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "Chapter8" 3 | version = "1.0.0" 4 | 5 | [dependencies] 6 | nix = "0.10" 7 | thread-id = "3.3" 8 | lazy_static = "1.0" 9 | rand = "0.4.2" 10 | 11 | [[bin]] 12 | name = "process_a" 13 | path = "process_a.rs" 14 | 15 | [[bin]] 16 | name = "process_b" 17 | path = "process_b.rs" 18 | 19 | [[bin]] 20 | name = "fork1" 21 | path = "fork1.rs" 22 | 23 | [[bin]] 24 | name = "fork2" 25 | path = "fork2.rs" 26 | 27 | [[bin]] 28 | name = "fork3" 29 | path = "fork3.rs" 30 | 31 | [[bin]] 32 | name = "thread1" 33 | path = "thread1.rs" 34 | 35 | [[bin]] 36 | name = "thread2" 37 | path = "thread2.rs" 38 | 39 | [[bin]] 40 | name = "thread3" 41 | path = "thread3.rs" 42 | 43 | [[bin]] 44 | name = "thread4" 45 | path = "thread4.rs" 46 | 47 | [[bin]] 48 | name = "thread5" 49 | path = "thread5.rs" 50 | 51 | [[bin]] 52 | name = "share1" 53 | path = "share1.rs" 54 | 55 | [[bin]] 56 | name = "share2" 57 | path = "share2.rs" 58 | 59 | [[bin]] 60 | name = "share3" 61 | path = "share3.rs" 62 | 63 | [[bin]] 64 | name = "share4" 65 | path = "share4.rs" 66 | 67 | [[bin]] 68 | name = "share5" 69 | path = "share5.rs" 70 | 71 | [[bin]] 72 | name = "share6" 73 | path = "share6.rs" 74 | 75 | [[bin]] 76 | name = "share7" 77 | path = "share7.rs" 78 | 79 | [[bin]] 80 | name = "pattern1" 81 | path = "pattern1.rs" 82 | 83 | [[bin]] 84 | name = "pattern2" 85 | path = "pattern2.rs" 86 | 87 | [[bin]] 88 | name = "pattern3" 89 | path = "pattern3.rs" 90 | 91 | -------------------------------------------------------------------------------- /Chapter08/fork1.rs: -------------------------------------------------------------------------------- 1 | extern crate nix; 2 | use nix::unistd::{fork,ForkResult}; 3 | use std::{thread,time}; 4 | use std::process; 5 | 6 | fn main() { 7 | let mut children = Vec::new(); 8 | for _ in 0..3 { 9 | match fork().expect("fork failed") { 10 | ForkResult::Parent{ child: pid } => { children.push(pid); } 11 | ForkResult::Child => { 12 | let t = time::Duration::from_millis(1000); 13 | loop { 14 | println!("child process #{}", process::id()); 15 | thread::sleep(t); 16 | } 17 | } 18 | } 19 | } 20 | 21 | let t = time::Duration::from_millis(1000); 22 | loop { 23 | println!("parent process #{}", process::id()); 24 | thread::sleep(t); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter08/fork2.rs: -------------------------------------------------------------------------------- 1 | extern crate nix; 2 | use nix::unistd::{fork}; 3 | use std::{thread,time}; 4 | 5 | fn main() { 6 | let mut big_data: Vec = Vec::with_capacity(200000000); 7 | big_data.push(1); 8 | big_data.push(2); 9 | big_data.push(3); 10 | 11 | //Both sides of the fork, will continue to fork 12 | //This is called a fork bomb 13 | for _ in 0..9 { 14 | fork().expect("fork failed"); 15 | } 16 | //2^9 = 512 17 | 18 | let t = time::Duration::from_millis(1000); 19 | loop { 20 | big_data[2]; 21 | thread::sleep(t); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Chapter08/fork3.rs: -------------------------------------------------------------------------------- 1 | extern crate nix; 2 | use nix::unistd::{fork,ForkResult}; 3 | use std::{thread,time}; 4 | use std::process; 5 | use std::io::prelude::*; 6 | use std::net::TcpListener; 7 | 8 | fn serve(listener: TcpListener) -> ! { 9 | for stream in listener.incoming() { 10 | let mut buffer = [0; 2048]; 11 | let mut tcp = stream.unwrap(); 12 | tcp.read(&mut buffer).expect("tcp read failed"); 13 | let response = format!("respond from #{}\n", process::id()); 14 | tcp.write(response.as_bytes()).expect("tcp write failed"); 15 | } 16 | panic!("unreachable"); 17 | } 18 | 19 | fn main() { 20 | let listener = TcpListener::bind("127.0.0.1:8888").unwrap(); 21 | let mut children = Vec::new(); 22 | 23 | for _ in 0..3 { 24 | match fork().expect("fork failed") { 25 | ForkResult::Parent{ child: pid } => { children.push(pid); } 26 | ForkResult::Child => { 27 | serve(listener) 28 | } 29 | } 30 | } 31 | 32 | let t = time::Duration::from_millis(1000); 33 | loop { 34 | thread::sleep(t); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Chapter08/pattern1.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::sync::mpsc::{channel}; 3 | use std::time; 4 | 5 | fn main() { 6 | let (pinginsend,pinginrecv) = channel(); 7 | let (pingoutsend,pingoutrecv) = channel(); 8 | let mut ping = 1; 9 | thread::spawn(move || { 10 | let t = time::Duration::from_millis(1000); 11 | loop { 12 | let n = pinginrecv.recv().unwrap(); 13 | ping += n; 14 | println!("ping {}", ping); 15 | thread::sleep(t); 16 | pingoutsend.send(ping).unwrap(); 17 | } 18 | }); 19 | 20 | let (ponginsend,ponginrecv) = channel(); 21 | let (pongoutsend,pongoutrecv) = channel(); 22 | let mut pong = 2; 23 | thread::spawn(move || { 24 | let t = time::Duration::from_millis(1000); 25 | loop { 26 | let n = ponginrecv.recv().unwrap(); 27 | pong += n; 28 | println!("pong {}", pong); 29 | thread::sleep(t); 30 | pongoutsend.send(pong).unwrap(); 31 | } 32 | }); 33 | 34 | let mut d = 3; 35 | loop { 36 | pinginsend.send(d).unwrap(); 37 | d = pingoutrecv.recv().unwrap(); 38 | ponginsend.send(d).unwrap(); 39 | d = pongoutrecv.recv().unwrap(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Chapter08/pattern2.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::sync::mpsc::{channel,Sender,Receiver}; 3 | use std::time; 4 | extern crate rand; 5 | 6 | fn new_ping() -> (Sender, Receiver) { 7 | let (pinginsend,pinginrecv) = channel(); 8 | let (pingoutsend,pingoutrecv) = channel(); 9 | let mut ping = 1; 10 | thread::spawn(move || { 11 | let t = time::Duration::from_millis(1000); 12 | loop { 13 | let n = pinginrecv.recv().unwrap(); 14 | ping += n; 15 | println!("ping {}", ping); 16 | thread::sleep(t); 17 | pingoutsend.send(ping).unwrap(); 18 | } 19 | }); 20 | (pinginsend, pingoutrecv) 21 | } 22 | 23 | fn new_pong() -> (Sender, Receiver) { 24 | let (ponginsend,ponginrecv) = channel(); 25 | let (pongoutsend,pongoutrecv) = channel(); 26 | let mut pong = 2; 27 | thread::spawn(move || { 28 | let t = time::Duration::from_millis(1000); 29 | loop { 30 | let n = ponginrecv.recv().unwrap(); 31 | pong += n; 32 | println!("pong {}", pong); 33 | thread::sleep(t); 34 | pongoutsend.send(pong).unwrap(); 35 | } 36 | }); 37 | (ponginsend, pongoutrecv) 38 | } 39 | 40 | fn main() { 41 | let pings = vec![new_ping(), new_ping(), new_ping()]; 42 | let pongs = vec![new_pong(), new_pong(), new_pong()]; 43 | loop { 44 | let mut d = 3; 45 | 46 | let (ref pingin,ref pingout) = pings[(rand::random::() % 3) as usize]; 47 | pingin.send(d).unwrap(); 48 | d = pingout.recv().unwrap(); 49 | 50 | let (ref pongin,ref pongout) = pongs[(rand::random::() % 3) as usize]; 51 | pongin.send(d).unwrap(); 52 | pongout.recv().unwrap(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Chapter08/pattern3.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::sync::mpsc::{channel,Sender,Receiver}; 3 | use std::time; 4 | extern crate rand; 5 | 6 | enum Address { 7 | Ping, 8 | Pong 9 | } 10 | enum Message { 11 | PingPlus(u64), 12 | PongPlus(u64), 13 | } 14 | 15 | fn new_ping() -> (Sender, Receiver<(Address,Message)>) { 16 | let (pinginsend,pinginrecv) = channel(); 17 | let (pingoutsend,pingoutrecv) = channel(); 18 | let mut ping = 1; 19 | thread::spawn(move || { 20 | let t = time::Duration::from_millis(1000); 21 | loop { 22 | let msg = pinginrecv.recv().unwrap(); 23 | match msg { 24 | Message::PingPlus(n) => { ping += n; }, 25 | _ => panic!("Unexpected message") 26 | } 27 | println!("ping {}", ping); 28 | thread::sleep(t); 29 | pingoutsend.send(( 30 | Address::Pong, 31 | Message::PongPlus(ping) 32 | )).unwrap(); 33 | pingoutsend.send(( 34 | Address::Pong, 35 | Message::PongPlus(ping) 36 | )).unwrap(); 37 | } 38 | }); 39 | (pinginsend, pingoutrecv) 40 | } 41 | 42 | fn new_pong() -> (Sender, Receiver<(Address,Message)>) { 43 | let (ponginsend,ponginrecv) = channel(); 44 | let (pongoutsend,pongoutrecv) = channel(); 45 | let mut pong = 1; 46 | thread::spawn(move || { 47 | let t = time::Duration::from_millis(1000); 48 | loop { 49 | let msg = ponginrecv.recv().unwrap(); 50 | match msg { 51 | Message::PongPlus(n) => { pong += n; }, 52 | _ => panic!("Unexpected message") 53 | } 54 | println!("pong {}", pong); 55 | thread::sleep(t); 56 | pongoutsend.send(( 57 | Address::Ping, 58 | Message::PingPlus(pong) 59 | )).unwrap(); 60 | pongoutsend.send(( 61 | Address::Ping, 62 | Message::PingPlus(pong) 63 | )).unwrap(); 64 | } 65 | }); 66 | (ponginsend, pongoutrecv) 67 | } 68 | 69 | fn main() { 70 | let pings = vec![new_ping(), new_ping(), new_ping()]; 71 | let pongs = vec![new_pong(), new_pong(), new_pong()]; 72 | 73 | //Start the action 74 | pings[0].0.send(Message::PingPlus(1)).unwrap(); 75 | 76 | //This thread will be the router 77 | let t = time::Duration::from_millis(10); 78 | loop { 79 | let mut mail = Vec::new(); 80 | 81 | for (_,r) in pings.iter() { 82 | for (addr,msg) in r.try_iter() { 83 | mail.push((addr,msg)); 84 | } 85 | } 86 | for (_,r) in pongs.iter() { 87 | for (addr,msg) in r.try_iter() { 88 | mail.push((addr,msg)); 89 | } 90 | } 91 | 92 | for (addr,msg) in mail.into_iter() { 93 | match addr { 94 | Address::Ping => { 95 | let (ref s,_) = pings[(rand::random::() as usize) % pings.len()]; 96 | s.send(msg).unwrap(); 97 | }, 98 | Address::Pong => { 99 | let (ref s,_) = pongs[(rand::random::() as usize) % pongs.len()]; 100 | s.send(msg).unwrap(); 101 | } 102 | } 103 | } 104 | 105 | thread::sleep(t); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Chapter08/process_a.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | use std::env::current_exe; 3 | 4 | fn main() { 5 | let path = current_exe() 6 | .expect("could not find current executable"); 7 | let path = path.with_file_name("process_b"); 8 | 9 | let mut children = Vec::new(); 10 | for _ in 0..3 { 11 | children.push( 12 | Command::new(path.as_os_str()) 13 | .spawn() 14 | .expect("failed to execute process") 15 | ); 16 | } 17 | 18 | for mut c in children { 19 | c.wait() 20 | .expect("failed to wait on child"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Chapter08/process_b.rs: -------------------------------------------------------------------------------- 1 | use std::{thread,time}; 2 | use std::process; 3 | 4 | fn main() { 5 | let t = time::Duration::from_millis(1000); 6 | loop { 7 | println!("process b #{}", process::id()); 8 | thread::sleep(t); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Chapter08/share1.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | fn main() { 4 | let a = vec![1, 2, 3]; 5 | 6 | thread::spawn(move || { 7 | println!("a = {:?}", a); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /Chapter08/share2.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | fn main() { 4 | let a = vec![1, 2, 3]; 5 | 6 | thread::spawn(move || { 7 | println!("a = {:?}", a); 8 | }); 9 | 10 | /* captures moved variable a 11 | thread::spawn(move || { 12 | println!("a = {:?}", a); 13 | }); 14 | */ 15 | } 16 | -------------------------------------------------------------------------------- /Chapter08/share3.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | fn main() { 4 | static A: [u8; 100] = [22; 100]; 5 | 6 | thread::spawn(|| { 7 | A[3]; 8 | }); 9 | 10 | thread::spawn(|| { 11 | A[3] 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /Chapter08/share4.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | #[macro_use] extern crate lazy_static; 3 | 4 | lazy_static! { 5 | static ref A: Vec = { 6 | vec![1, 2, 3] 7 | }; 8 | } 9 | 10 | fn main() { 11 | thread::spawn(|| { 12 | A[1]; 13 | }); 14 | 15 | thread::spawn(|| { 16 | A[2]; 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /Chapter08/share5.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::sync::{Arc}; 3 | 4 | fn main() { 5 | let a = Arc::new(vec![1, 2, 3]); 6 | 7 | { 8 | let a = Arc::clone(&a); 9 | thread::spawn(move || { 10 | a[1]; 11 | }); 12 | } 13 | 14 | { 15 | let a = Arc::clone(&a); 16 | thread::spawn(move || { 17 | a[1]; 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Chapter08/share6.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::sync::{Arc,Mutex}; 3 | 4 | fn main() { 5 | let a = Arc::new(Mutex::new(vec![1, 2, 3])); 6 | { 7 | let a = Arc::clone(&a); 8 | thread::spawn(move || { 9 | let mut a = a.lock().unwrap(); 10 | (*a)[1] = 2; 11 | }); 12 | } 13 | 14 | { 15 | let a = Arc::clone(&a); 16 | thread::spawn(move || { 17 | let mut a = a.lock().unwrap(); 18 | (*a)[1] = 3; 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Chapter08/share7.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | struct MyBox(u8); 4 | unsafe impl Send for MyBox {} 5 | unsafe impl Sync for MyBox {} 6 | 7 | static A: MyBox = MyBox(22); 8 | 9 | fn main() { 10 | thread::spawn(move || { 11 | A.0 12 | }); 13 | 14 | thread::spawn(move || { 15 | A.0 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /Chapter08/thread1.rs: -------------------------------------------------------------------------------- 1 | use std::{thread,time}; 2 | use std::process; 3 | extern crate thread_id; 4 | 5 | fn main() { 6 | for _ in 0..3 { 7 | thread::spawn(|| { 8 | let t = time::Duration::from_millis(1000); 9 | loop { 10 | println!("child thread #{}:{}", process::id(), thread_id::get()); 11 | thread::sleep(t); 12 | } 13 | }); 14 | } 15 | 16 | let t = time::Duration::from_millis(1000); 17 | loop { 18 | println!("parent thread #{}:{}", process::id(), thread_id::get()); 19 | thread::sleep(t); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Chapter08/thread2.rs: -------------------------------------------------------------------------------- 1 | use std::{thread,time}; 2 | use std::sync::{Mutex, Arc}; 3 | 4 | fn main() { 5 | let mut big_data: Vec = Vec::with_capacity(200000000); 6 | big_data.push(1); 7 | big_data.push(2); 8 | big_data.push(3); 9 | let big_data = Arc::new(Mutex::new(big_data)); 10 | 11 | for _ in 0..512 { 12 | let big_data = Arc::clone(&big_data); 13 | thread::spawn(move || { 14 | let t = time::Duration::from_millis(1000); 15 | loop { 16 | let d = big_data.lock().unwrap(); 17 | (*d)[2]; 18 | thread::sleep(t); 19 | } 20 | }); 21 | } 22 | 23 | let t = time::Duration::from_millis(1000); 24 | loop { 25 | thread::sleep(t); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Chapter08/thread3.rs: -------------------------------------------------------------------------------- 1 | use std::{thread,time}; 2 | use std::process; 3 | extern crate thread_id; 4 | use std::io::prelude::*; 5 | use std::net::{TcpListener,TcpStream}; 6 | use std::sync::{Arc,Mutex}; 7 | 8 | fn serve(incoming: Arc>>) { 9 | let t = time::Duration::from_millis(10); 10 | loop { 11 | { 12 | let mut incoming = incoming.lock().unwrap(); 13 | for stream in incoming.iter() { 14 | let mut buffer = [0; 2048]; 15 | let mut tcp = stream; 16 | tcp.read(&mut buffer).expect("tcp read failed"); 17 | let response = format!("respond from #{}:{}\n", process::id(), thread_id::get()); 18 | tcp.write(response.as_bytes()).expect("tcp write failed"); 19 | } 20 | incoming.clear(); 21 | } 22 | thread::sleep(t); 23 | } 24 | } 25 | 26 | fn main() { 27 | let listener = TcpListener::bind("127.0.0.1:8888").unwrap(); 28 | let incoming = Vec::new(); 29 | let incoming = Arc::new(Mutex::new(incoming)); 30 | 31 | for _ in 0..3 { 32 | let incoming = Arc::clone(&incoming); 33 | thread::spawn(move || { 34 | serve(incoming); 35 | }); 36 | } 37 | 38 | for stream in listener.incoming() { 39 | let mut incoming = incoming.lock().unwrap(); 40 | (*incoming).push(stream.unwrap()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Chapter08/thread4.rs: -------------------------------------------------------------------------------- 1 | use std::{thread,time}; 2 | use std::process; 3 | use std::io::prelude::*; 4 | extern crate thread_id; 5 | use std::net::{TcpListener,TcpStream}; 6 | use std::sync::mpsc::{channel,Receiver}; 7 | use std::collections::VecDeque; 8 | 9 | fn serve(receiver: Receiver) { 10 | let t = time::Duration::from_millis(10); 11 | loop { 12 | let mut tcp = receiver.recv().unwrap(); 13 | let mut buffer = [0; 2048]; 14 | tcp.read(&mut buffer).expect("tcp read failed"); 15 | let response = format!("respond from #{}:{}\n", process::id(), thread_id::get()); 16 | tcp.write(response.as_bytes()).expect("tcp write failed"); 17 | thread::sleep(t); 18 | } 19 | } 20 | 21 | fn main() { 22 | let listener = TcpListener::bind("127.0.0.1:8888").unwrap(); 23 | let mut channels = VecDeque::new(); 24 | 25 | for _ in 0..3 { 26 | let (sender, receiver) = channel(); 27 | channels.push_back(sender); 28 | thread::spawn(move || { 29 | serve(receiver); 30 | }); 31 | } 32 | 33 | for stream in listener.incoming() { 34 | let round_robin = channels.pop_front().unwrap(); 35 | round_robin.send(stream.unwrap()).unwrap(); 36 | channels.push_back(round_robin); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Chapter08/thread5.rs: -------------------------------------------------------------------------------- 1 | use std::{thread,time}; 2 | extern crate rand; 3 | use std::sync::{Arc,Mutex}; 4 | #[macro_use] extern crate lazy_static; 5 | 6 | lazy_static! { 7 | static ref NEURAL_NET_WEIGHTS: Vec>>> = { 8 | let mut nn = Vec::with_capacity(10000); 9 | for _ in 0..10000 { 10 | let mut mm = Vec::with_capacity(100); 11 | for _ in 0..100 { 12 | mm.push(rand::random::()); 13 | } 14 | let mm = Arc::new(Mutex::new(mm)); 15 | nn.push(mm); 16 | } 17 | nn 18 | }; 19 | } 20 | 21 | fn train() { 22 | let t = time::Duration::from_millis(100); 23 | loop { 24 | for _ in 0..100 { 25 | let update_position = rand::random::() % 1000000; 26 | let update_column = update_position / 10000; 27 | let update_row = update_position % 100; 28 | let update_value = rand::random::(); 29 | let mut update_column = NEURAL_NET_WEIGHTS[update_column as usize].lock().unwrap(); 30 | update_column[update_row as usize] = update_value; 31 | } 32 | thread::sleep(t); 33 | } 34 | } 35 | 36 | fn main() { 37 | let t = time::Duration::from_millis(1000); 38 | 39 | for _ in 0..500 { 40 | thread::spawn(train); 41 | } 42 | 43 | loop { 44 | thread::sleep(t); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Chapter09/.gitignore: -------------------------------------------------------------------------------- 1 | data.txt 2 | -------------------------------------------------------------------------------- /Chapter09/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "Chapter9" 3 | version = "1.0.0" 4 | 5 | [dependencies] 6 | flame = "0.2" 7 | rand = "0.4.2" 8 | requests = "0.0.30" 9 | rusty-machine = "0.5.4" 10 | procmacro = { path = "procmacro" } 11 | procmacro2 = { path = "procmacro2" } 12 | 13 | [[bin]] 14 | name = "performance_release_mode" 15 | path = "performance_release_mode.rs" 16 | 17 | [[bin]] 18 | name = "performance_omission1" 19 | path = "performance_omission1.rs" 20 | 21 | [[bin]] 22 | name = "performance_omission2" 23 | path = "performance_omission2.rs" 24 | 25 | [[bin]] 26 | name = "performance_profiling1" 27 | path = "performance_profiling1.rs" 28 | 29 | [[bin]] 30 | name = "performance_profiling2" 31 | path = "performance_profiling2.rs" 32 | 33 | [[bin]] 34 | name = "performance_profiling3" 35 | path = "performance_profiling3.rs" 36 | 37 | [[bin]] 38 | name = "performance_profiling4" 39 | path = "performance_profiling4.rs" 40 | 41 | [[bin]] 42 | name = "performance_constant" 43 | path = "performance_constant.rs" 44 | 45 | [[bin]] 46 | name = "performance_logarithmic" 47 | path = "performance_logarithmic.rs" 48 | 49 | [[bin]] 50 | name = "performance_polynomial1" 51 | path = "performance_polynomial1.rs" 52 | 53 | [[bin]] 54 | name = "performance_polynomial2" 55 | path = "performance_polynomial2.rs" 56 | 57 | [[bin]] 58 | name = "performance_polynomial3" 59 | path = "performance_polynomial3.rs" 60 | 61 | [[bin]] 62 | name = "performance_polynomial4" 63 | path = "performance_polynomial4.rs" 64 | 65 | [[bin]] 66 | name = "performance_exponential" 67 | path = "performance_exponential.rs" 68 | 69 | [[bin]] 70 | name = "performance_reference" 71 | path = "performance_reference.rs" 72 | 73 | [[bin]] 74 | name = "debugging_result" 75 | path = "debugging_result.rs" 76 | 77 | [[bin]] 78 | name = "debugging_heartbeat" 79 | path = "debugging_heartbeat.rs" 80 | 81 | [[bin]] 82 | name = "debugging_buggy_worker" 83 | path = "debugging_buggy_worker.rs" 84 | 85 | [[bin]] 86 | name = "debugging_assert" 87 | path = "debugging_assert.rs" 88 | 89 | [[bin]] 90 | name = "metaprogramming_grammar" 91 | path = "metaprogramming_grammar.rs" 92 | 93 | [[bin]] 94 | name = "metaprogramming_ebnf" 95 | path = "metaprogramming_ebnf.rs" 96 | 97 | [[bin]] 98 | name = "metaprogramming_procmacro" 99 | path = "metaprogramming_procmacro.rs" 100 | 101 | [[bin]] 102 | name = "metaprogramming_procmacro2" 103 | path = "metaprogramming_procmacro2.rs" 104 | -------------------------------------------------------------------------------- /Chapter09/README.md: -------------------------------------------------------------------------------- 1 | This chapter makes use of Cargo Profiler and valgrind. 2 | They will not be installed through the normal cargo build process and need separate installation. 3 | 4 | [Cargo Profiler](https://github.com/kernelmachine/cargo-profiler) installation instructions are available on Github. 5 | -------------------------------------------------------------------------------- /Chapter09/debugging_assert.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | fn debug_precondition(n: u64) -> u64 { 4 | debug_assert!(n < 100); 5 | n * n 6 | } 7 | 8 | fn debug_postcondition(n: u64) -> u64 { 9 | let r = n * n; 10 | debug_assert!(r > 10); 11 | r 12 | } 13 | 14 | fn runtime_precondition(n: u64) -> Result { 15 | if !(n<100) { return Err(()) }; 16 | Ok(n * n) 17 | } 18 | 19 | fn runtime_postcondition(n: u64) -> Result { 20 | let r = n * n; 21 | if !(r>10) { return Err(()) }; 22 | Ok(r) 23 | } 24 | 25 | fn main() { 26 | //inward facing code should assert expectations 27 | debug_precondition(5); 28 | debug_postcondition(5); 29 | 30 | //outward facing code should handle errors 31 | let mut s = String::new(); 32 | println!("Please input a positive integer greater or equal to 4:"); 33 | io::stdin().read_line(&mut s).expect("error reading input"); 34 | let i = s.trim().parse::().expect("error parsing input as integer"); 35 | runtime_precondition(i).expect("runtime precondition violated"); 36 | runtime_postcondition(i).expect("runtime postcondition violated"); 37 | } 38 | -------------------------------------------------------------------------------- /Chapter09/debugging_buggy_worker.rs: -------------------------------------------------------------------------------- 1 | use std::{thread,time,process}; 2 | 3 | fn main() { 4 | let life_expectancy = process::id() % 8; 5 | let t = time::Duration::from_millis(1000); 6 | for _ in 0..life_expectancy { 7 | thread::sleep(t); 8 | } 9 | println!("process {} dies unexpectedly.", process::id()); 10 | } 11 | -------------------------------------------------------------------------------- /Chapter09/debugging_heartbeat.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | use std::env::current_exe; 3 | use std::{thread,time}; 4 | 5 | fn main() { 6 | let path = current_exe() 7 | .expect("could not find current executable"); 8 | let path = path.with_file_name("debugging_buggy_worker"); 9 | 10 | let mut children = Vec::new(); 11 | for _ in 0..3 { 12 | children.push( 13 | Command::new(path.as_os_str()) 14 | .spawn() 15 | .expect("failed to spawn child") 16 | ); 17 | } 18 | 19 | let t = time::Duration::from_millis(1000); 20 | loop { 21 | thread::sleep(t); 22 | for ci in 0..children.len() { 23 | let is_dead = children[ci].try_wait().expect("failed to try_wait"); 24 | if let Some(_exit_code) = is_dead { 25 | children[ci] = Command::new(path.as_os_str()) 26 | .spawn() 27 | .expect("failed to spawn child"); 28 | println!("starting a new process from parent."); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Chapter09/debugging_result.rs: -------------------------------------------------------------------------------- 1 | fn expect_1or2or_other(n: u64) -> Option { 2 | match n { 3 | 1|2 => Some(n), 4 | _ => None 5 | } 6 | } 7 | 8 | fn expect_1or2or_error(n: u64) -> Result { 9 | match n { 10 | 1|2 => Ok(n), 11 | _ => Err(()) 12 | } 13 | } 14 | 15 | fn mixed_1or2() -> Result<(),()> { 16 | expect_1or2or_other(1); 17 | expect_1or2or_other(2); 18 | expect_1or2or_other(3); 19 | 20 | expect_1or2or_error(1)?; 21 | expect_1or2or_error(2)?; 22 | expect_1or2or_error(3).unwrap_or(222); 23 | 24 | Ok(()) 25 | } 26 | 27 | use std::fs::File; 28 | use std::io::prelude::*; 29 | use std::io; 30 | 31 | fn lots_of_io() -> io::Result<()> { 32 | { 33 | let mut file = File::create("data.txt")?; 34 | file.write_all(b"data\ndata\ndata")?; 35 | } 36 | { 37 | let mut file = File::open("data.txt")?; 38 | let mut data = String::new(); 39 | file.read_to_string(&mut data)?; 40 | println!("{}", data); 41 | } 42 | 43 | Ok(()) 44 | } 45 | 46 | fn main() { 47 | mixed_1or2().expect("mixed 1 or 2 is OK."); 48 | 49 | lots_of_io().expect("lots of io is OK."); 50 | } 51 | -------------------------------------------------------------------------------- /Chapter09/metaprogramming_ebnf.rs: -------------------------------------------------------------------------------- 1 | //this is a grammar sequence 2 | macro_rules! abc { 3 | (a b c) => { println!("'a b c' is the only correct syntax.") }; 4 | } 5 | 6 | //this is a grammar alternative 7 | macro_rules! a_or_b { 8 | (a) => { println!("'a' is one correct syntax.") }; 9 | (b) => { println!("'b' is also correct syntax.") }; 10 | } 11 | 12 | //this is a grammar alternative 13 | macro_rules! abc_or_aaa { 14 | (a b c) => { println!("'a b c' is one correct syntax.") }; 15 | (a a a) => { println!("'a a a' is also correct syntax.") }; 16 | } 17 | 18 | //this is a grammar sequence matching many of one token 19 | macro_rules! many_a { 20 | ( $($a:ident)* ) => {{ $( print!("one {} ", stringify!($a)); )* println!(""); }}; 21 | ( $($a:ident),* ) => {{ $( print!("one {} comma ", stringify!($a)); )* println!(""); }}; 22 | } 23 | 24 | fn main() { 25 | abc!(a b c); 26 | 27 | a_or_b!(a); 28 | a_or_b!(b); 29 | 30 | abc_or_aaa!(a b c); 31 | abc_or_aaa!(a a a); 32 | 33 | many_a!(a a a); 34 | many_a!(a, a, a); 35 | } 36 | -------------------------------------------------------------------------------- /Chapter09/metaprogramming_grammar.rs: -------------------------------------------------------------------------------- 1 | macro_rules! match_tt { 2 | ($e: tt) => { println!("match_tt: {}", stringify!($e)) } 3 | } 4 | 5 | macro_rules! match_ident { 6 | ($e: ident) => { println!("match_ident: {}", stringify!($e)) } 7 | } 8 | 9 | macro_rules! match_expr { 10 | ($e: expr) => { println!("match_expr: {}", stringify!($e)) } 11 | } 12 | 13 | macro_rules! match_ty { 14 | ($e: ty) => { println!("match_ty: {}", stringify!($e)) } 15 | } 16 | 17 | macro_rules! match_stmt { 18 | ($e: stmt) => { println!("match_stmt: {}", stringify!($e)) } 19 | } 20 | 21 | macro_rules! match_block { 22 | ($e: block) => { println!("match_block: {}", stringify!($e)) } 23 | } 24 | 25 | macro_rules! match_item { 26 | ($e: item) => { println!("match_item: {}", stringify!($e)) } 27 | } 28 | 29 | macro_rules! match_pat { 30 | ($e: pat) => { println!("match_pat: {}", stringify!($e)) } 31 | } 32 | 33 | macro_rules! match_path { 34 | ($e: path) => { println!("match_path: {}", stringify!($e)) } 35 | } 36 | 37 | macro_rules! match_meta { 38 | ($e: meta) => { println!("match_meta: {}", stringify!($e)) } 39 | } 40 | 41 | fn main() { 42 | match_tt!(a); 43 | match_tt!(let); 44 | match_tt!(+); 45 | 46 | match_ident!(a); 47 | match_ident!(bcd); 48 | match_ident!(_def); 49 | 50 | match_expr!(1.2); 51 | match_expr!(bcd); 52 | match_expr!(1.2 + bcd / "b" - [1, 3, 4] .. vec![1, 2, 3]); 53 | 54 | match_ty!(A); 55 | match_ty!(B + 'static); 56 | match_ty!(A<&(B + 'b),&mut (C + 'c)> + 'static); 57 | 58 | match_stmt!(let x = y); 59 | match_stmt!(()); 60 | match_stmt!(fn f(){}); 61 | 62 | match_block!({}); 63 | match_block!({1; 2}); 64 | match_block!({1; 2 + 3}); 65 | 66 | match_item!(struct A(u64);); 67 | match_item!(enum B { C, D }); 68 | match_item!(fn C(n: NotAType) -> F>>> { a + b }); 69 | 70 | match_pat!(_); 71 | match_pat!(1); 72 | match_pat!(A {b, c:D( d@3 )} ); 73 | 74 | match_path!(A); 75 | match_path!(::A); 76 | match_path!(std::A); 77 | match_path!(a::); 78 | 79 | match_meta!(A); 80 | match_meta!(Property(B,C)); 81 | } 82 | -------------------------------------------------------------------------------- /Chapter09/metaprogramming_procmacro.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_non_items)] 2 | #![feature(use_extern_macros)] 3 | extern crate procmacro; 4 | 5 | fn main() { 6 | let _ = procmacro::f!(); 7 | } 8 | -------------------------------------------------------------------------------- /Chapter09/metaprogramming_procmacro2.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_non_items)] 2 | #![feature(use_extern_macros)] 3 | extern crate procmacro2; 4 | 5 | fn main() { 6 | procmacro2::misc_syntax!( 7 | where while abcd : u64 >> 1 + 2 * 3; where T: 'x + A;[M];A::f 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /Chapter09/performance_constant.rs: -------------------------------------------------------------------------------- 1 | fn allocate() -> [u64; 1000] { 2 | [22; 1000] 3 | } 4 | 5 | fn flop(x: f64, y: f64) -> f64 { 6 | x * y 7 | } 8 | 9 | fn lookup(x: &[u64; 1000]) -> u64 { 10 | x[234] * x[345] 11 | } 12 | 13 | fn main() { 14 | let mut data = allocate(); 15 | for _ in 0..1000 { 16 | //constant size memory allocation 17 | data = allocate(); 18 | } 19 | 20 | for _ in 0..1000000 { 21 | //reference data 22 | lookup(&data); 23 | } 24 | 25 | for _ in 0..1000000 { 26 | //floating point operation 27 | flop(2.0, 3.0); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Chapter09/performance_exponential.rs: -------------------------------------------------------------------------------- 1 | fn bomb(n: u64) -> u64 { 2 | if n > 0 { 3 | bomb(n-1); 4 | bomb(n-1); 5 | } 6 | n 7 | } 8 | 9 | fn main() { 10 | bomb(1000); 11 | } 12 | -------------------------------------------------------------------------------- /Chapter09/performance_logarithmic.rs: -------------------------------------------------------------------------------- 1 | extern crate rand; 2 | extern crate flame; 3 | use std::fs::File; 4 | 5 | fn main() { 6 | 7 | let mut data = vec![0; 1000]; 8 | for di in 0..data.len() { 9 | data[di] = rand::random::(); 10 | } 11 | 12 | flame::start("sort n=1000"); 13 | data.sort(); 14 | flame::end("sort n=1000"); 15 | 16 | flame::start("binary search n=1000 100 times"); 17 | for _ in 0..100 { 18 | let c = rand::random::(); 19 | data.binary_search(&c).ok(); 20 | } 21 | flame::end("binary search n=1000 100 times"); 22 | 23 | let mut data = vec![0; 10000]; 24 | for di in 0..data.len() { 25 | data[di] = rand::random::(); 26 | } 27 | 28 | flame::start("sort n=10000"); 29 | data.sort(); 30 | flame::end("sort n=10000"); 31 | 32 | flame::start("binary search n=10000 100 times"); 33 | for _ in 0..100 { 34 | let c = rand::random::(); 35 | data.binary_search(&c).ok(); 36 | } 37 | flame::end("binary search n=10000 100 times"); 38 | 39 | flame::dump_html(&mut File::create("flame-graph.html").unwrap()).unwrap(); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Chapter09/performance_omission1.rs: -------------------------------------------------------------------------------- 1 | extern crate flame; 2 | use std::fs::File; 3 | 4 | fn main() { 5 | let v: Vec = vec![2; 1000000]; 6 | 7 | flame::start("Iterator .collect"); 8 | let mut _z = vec![]; 9 | for _ in 0..1000 { 10 | _z = v.iter().map(|x| x*x).collect::>(); 11 | } 12 | flame::end("Iterator .collect"); 13 | 14 | flame::start("Iterator iterate"); 15 | for _ in 0..1000 { 16 | v.iter().map(|x| x * x).for_each(drop); 17 | } 18 | flame::end("Iterator iterate"); 19 | 20 | flame::dump_html(&mut File::create("flame-graph.html").unwrap()).unwrap(); 21 | } 22 | -------------------------------------------------------------------------------- /Chapter09/performance_omission2.rs: -------------------------------------------------------------------------------- 1 | use std::mem::forget; 2 | 3 | fn main() { 4 | for _ in 0..10000 { 5 | let mut a = vec![2; 10000000]; 6 | a[2] = 2; 7 | forget(a); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter09/performance_polynomial1.rs: -------------------------------------------------------------------------------- 1 | 2 | fn main() { 3 | for _ in 0..1000 { 4 | //O(n) 5 | //n = 1000 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Chapter09/performance_polynomial2.rs: -------------------------------------------------------------------------------- 1 | 2 | fn main() { 3 | for _ in 0..1000 { 4 | for _ in 0..1000 { 5 | //O(n^2) 6 | //n = 1000 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter09/performance_polynomial3.rs: -------------------------------------------------------------------------------- 1 | fn a(n: u64) { 2 | //Is this O(n)? 3 | for _ in 0..n { 4 | b(n) 5 | } 6 | } 7 | 8 | fn b(n: u64) { 9 | //Is this O(n)? 10 | for _ in 0..n { 11 | c(n) 12 | } 13 | } 14 | 15 | fn c(n: u64) { 16 | //This is O(n)? 17 | for _ in 0..n { 18 | let _ = 1 + 1; 19 | } 20 | } 21 | 22 | fn main() { 23 | //What time complexity is this? 24 | a(1000) 25 | } 26 | -------------------------------------------------------------------------------- /Chapter09/performance_polynomial4.rs: -------------------------------------------------------------------------------- 1 | extern crate rusty_machine; 2 | use rusty_machine::linalg::{Matrix,Vector}; 3 | use rusty_machine::learning::gp::{GaussianProcess,ConstMean}; 4 | use rusty_machine::learning::toolkit::kernel; 5 | use rusty_machine::learning::SupModel; 6 | 7 | fn main() { 8 | let inputs = Matrix::new(3,3,vec![1.1,1.2,1.3,2.1,2.2,2.3,3.1,3.2,3.3]); 9 | let targets = Vector::new(vec![0.1,0.8,0.3]); 10 | 11 | let test_inputs = Matrix::new(2,3, vec![1.2,1.3,1.4,2.2,2.3,2.4]); 12 | 13 | let ker = kernel::SquaredExp::new(2., 1.); 14 | let zero_mean = ConstMean::default(); 15 | let mut gp = GaussianProcess::new(ker, zero_mean, 0.5); 16 | 17 | gp.train(&inputs, &targets).unwrap(); 18 | let _ = gp.predict(&test_inputs).unwrap(); 19 | } 20 | -------------------------------------------------------------------------------- /Chapter09/performance_profiling1.rs: -------------------------------------------------------------------------------- 1 | use std::{thread,time}; 2 | 3 | fn initialization() { 4 | let t = time::Duration::from_millis(15000); 5 | thread::sleep(t); 6 | } 7 | 8 | fn work() { 9 | let t = time::Duration::from_millis(15000); 10 | loop { 11 | thread::sleep(t); 12 | println!("Work."); 13 | } 14 | } 15 | 16 | fn main() { 17 | initialization(); 18 | println!("Done initializing, start work."); 19 | work(); 20 | } 21 | -------------------------------------------------------------------------------- /Chapter09/performance_profiling2.rs: -------------------------------------------------------------------------------- 1 | use std::{thread,time}; 2 | 3 | fn initialization() -> Vec { 4 | let t = time::Duration::from_millis(15000); 5 | thread::sleep(t); 6 | println!("Initialize data."); 7 | vec![1, 2, 3] 8 | } 9 | 10 | fn work(x: i32) -> i32 { 11 | let t = time::Duration::from_millis(150); 12 | thread::sleep(t); 13 | println!("Work."); 14 | x * x 15 | } 16 | 17 | fn main() { 18 | 19 | for _ in 0..10 { 20 | let data = initialization(); 21 | data.iter().map(|x| work(*x)).for_each(drop); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Chapter09/performance_profiling3.rs: -------------------------------------------------------------------------------- 1 | extern crate flame; 2 | use std::fs::File; 3 | use std::{thread,time}; 4 | 5 | fn a() { 6 | flame::start("fn a"); 7 | let t = time::Duration::from_millis(1000); 8 | thread::sleep(t); 9 | b(); 10 | b(); 11 | b(); 12 | flame::end("fn a"); 13 | } 14 | 15 | fn b() { 16 | flame::start("fn b"); 17 | let t = time::Duration::from_millis(1000); 18 | thread::sleep(t); 19 | c(); 20 | c(); 21 | c(); 22 | flame::end("fn b"); 23 | } 24 | 25 | fn c() { 26 | flame::start("fn c"); 27 | let t = time::Duration::from_millis(1000); 28 | thread::sleep(t); 29 | flame::end("fn c"); 30 | } 31 | 32 | fn main() { 33 | flame::start("fn main"); 34 | let t = time::Duration::from_millis(1000); 35 | thread::sleep(t); 36 | a(); 37 | a(); 38 | a(); 39 | flame::end("fn main"); 40 | 41 | flame::dump_html(&mut File::create("flame-graph.html").unwrap()).unwrap(); 42 | } 43 | -------------------------------------------------------------------------------- /Chapter09/performance_profiling4.rs: -------------------------------------------------------------------------------- 1 | fn a(n: u64) -> u64 { 2 | if n>0 { 3 | b(n); 4 | b(n); 5 | } 6 | n * n 7 | } 8 | 9 | fn b(n: u64) -> u64 { 10 | c(n); 11 | c(n); 12 | n + 2 / 3 13 | } 14 | 15 | fn c(n: u64) -> u64 { 16 | a(n-1); 17 | a(n-1); 18 | vec![1, 2, 3].into_iter().map(|x| x+2).sum() 19 | } 20 | 21 | fn main() { 22 | a(6); 23 | } 24 | -------------------------------------------------------------------------------- /Chapter09/performance_reference.rs: -------------------------------------------------------------------------------- 1 | extern crate flame; 2 | use std::fs::File; 3 | 4 | fn byref(n: u64, data: &[u64; 1024]) { 5 | if n>0 { 6 | byref(n-1, data); 7 | byref(n-1, data); 8 | } 9 | } 10 | 11 | fn bycopy(n: u64, data: [u64; 1024]) { 12 | if n>0 { 13 | bycopy(n-1, data); 14 | bycopy(n-1, data); 15 | } 16 | } 17 | 18 | struct DataClonable([u64; 1024]); 19 | impl Clone for DataClonable { 20 | fn clone(&self) -> Self { 21 | let mut newdata = [0; 1024]; 22 | for i in 0..1024 { 23 | newdata[i] = self.0[i]; 24 | } 25 | DataClonable(newdata) 26 | } 27 | } 28 | fn byclone(n: u64, data: T) { 29 | if n>0 { 30 | byclone(n-1, data.clone()); 31 | byclone(n-1, data.clone()); 32 | } 33 | } 34 | 35 | fn main() { 36 | let data = [0; 1024]; 37 | flame::start("by reference"); 38 | byref(15, &data); 39 | flame::end("by reference"); 40 | 41 | let data = [0; 1024]; 42 | flame::start("by copy"); 43 | bycopy(15, data); 44 | flame::end("by copy"); 45 | 46 | let data = [0; 1024]; 47 | flame::start("by clone"); 48 | byclone(15, data); 49 | flame::end("by clone"); 50 | 51 | let data = DataClonable([0; 1024]); 52 | flame::start("by clone (with extras)"); 53 | //2^4 instead of 2^15!!!! 54 | byclone(4, data); 55 | flame::end("by clone (with extras)"); 56 | 57 | flame::dump_html(&mut File::create("flame-graph.html").unwrap()).unwrap(); 58 | } 59 | -------------------------------------------------------------------------------- /Chapter09/performance_release_mode.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut res: Vec> = vec![vec![0; 512]; 512]; 3 | for _ in 0..50 { 4 | for i in 1..511 { 5 | for j in 1..511 { 6 | res[j][i] = 2; 7 | res[j][i] += res[j-1][i-1]; 8 | res[j][i] += res[j][i-1]; 9 | res[j][i] += res[j+1][i-1]; 10 | res[j][i] += res[j-1][i]; 11 | res[j][i] += res[j][i]; 12 | res[j][i] += res[j+1][i]; 13 | res[j][i] += res[j-1][i+1]; 14 | res[j][i] += res[j][i+1]; 15 | res[j][i] += res[j+1][i+1]; 16 | res[j][i] /= 9; 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Chapter09/procmacro/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target 3 | -------------------------------------------------------------------------------- /Chapter09/procmacro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "procmacro" 3 | version = "1.0.0" 4 | 5 | [dependencies] 6 | syn = "0.12" 7 | quote = "0.4" 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /Chapter09/procmacro/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro)] 2 | #![crate_type = "proc-macro"] 3 | extern crate proc_macro; 4 | extern crate syn; 5 | #[macro_use] extern crate quote; 6 | use proc_macro::TokenStream; 7 | 8 | #[proc_macro] 9 | pub fn f(input: TokenStream) -> TokenStream { 10 | assert!(input.is_empty()); 11 | 12 | (quote! { 13 | 1 + 2 14 | }).into() 15 | } 16 | -------------------------------------------------------------------------------- /Chapter09/procmacro2/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target 3 | -------------------------------------------------------------------------------- /Chapter09/procmacro2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "procmacro2" 3 | version = "1.0.0" 4 | 5 | [dependencies] 6 | syn = "0.12" 7 | quote = "0.4" 8 | 9 | [lib] 10 | proc-macro = true 11 | -------------------------------------------------------------------------------- /Chapter09/procmacro2/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro)] 2 | #![crate_type = "proc-macro"] 3 | extern crate proc_macro; 4 | #[macro_use] extern crate syn; 5 | #[macro_use] extern crate quote; 6 | 7 | use proc_macro::TokenStream; 8 | use syn::{Ident, Type, Expr, WhereClause, TypeSlice, Path}; 9 | use syn::synom::Synom; 10 | 11 | struct MiscSyntax { 12 | id: Ident, 13 | ty: Type, 14 | expr: Expr, 15 | where_clause: WhereClause, 16 | type_slice: TypeSlice, 17 | path: Path 18 | } 19 | 20 | impl Synom for MiscSyntax { 21 | named!(parse -> Self, do_parse!( 22 | keyword!(where) >> 23 | keyword!(while) >> 24 | id: syn!(Ident) >> 25 | punct!(:) >> 26 | ty: syn!(Type) >> 27 | punct!(>>) >> 28 | expr: syn!(Expr) >> 29 | punct!(;) >> 30 | where_clause: syn!(WhereClause) >> 31 | punct!(;) >> 32 | type_slice: syn!(TypeSlice) >> 33 | punct!(;) >> 34 | path: syn!(Path) >> 35 | (MiscSyntax { id, ty, expr, where_clause, type_slice, path }) 36 | )); 37 | } 38 | 39 | #[proc_macro] 40 | pub fn misc_syntax(input: TokenStream) -> TokenStream { 41 | let m: MiscSyntax = syn::parse(input).expect("expected Miscellaneous Syntax"); 42 | let MiscSyntax { id, ty, expr, where_clause, type_slice, path } = m; 43 | 44 | (quote! { 45 | let #id: #ty = #expr; 46 | println!("variable = {}", #id); 47 | }).into() 48 | } 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Hands-On Functional Programming in Rust 5 | This is the code repository for [Hands-On Functional Programming in Rust](https://www.packtpub.com/application-development/hands-functional-programming-rust?utm_source=github&utm_medium=repository&utm_campaign=9781788839358), published by [Packt](https://www.packtpub.com/?utm_source=github). It contains all the supporting project files necessary to work through the book from start to finish. 6 | ## About the Book 7 | Functional Programming allows developers to divide programs into smaller, reusable components that ease the creation, testing, and maintenance of software as a whole. Combined with the power of Rust, you can develop robust and scalable applications that fulfill modern day software requirements. This book will help you discover all the Rust features that can be used to build software in a functional way. 8 | ## Instructions and Navigation 9 | All of the code is organized into folders. Each folder starts with a number followed by the application name. For example, Chapter02. 10 | 11 | All code files are present in their respective folders. 12 | 13 | The code will look like the following: 14 | ``` 15 | pub trait MotorController 16 | { 17 | fn init(&mut self, esp: ElevatorSpecification, est: ElevatorState); 18 | fn poll(&mut self, est: ElevatorState, dst: u64) -> MotorInput; 19 | } 20 | ``` 21 | 22 | In order to successfully read through this book and work out all the code samples, we expect readers to fulfill the following 23 | * We assume familiarity with the concepts from the first 10 chapters of Rust 24 | documentation (https:/​/​doc.​rust-​lang.​org/​book/​). Some of the material from 25 | these chapters is fairly advanced, so we will also explain that here when relevant. 26 | However, the knowledge of syntax and very basic features will be expected. 27 | * Clone the GitHub code repository and follow along. Tweak the examples and see 28 | what effects you can create. 29 | * Stay curious. Some of the keywords we mentioned could fill an entire book with 30 | unique content. Some of these topics presented are so pervasive that they have 31 | decent Wikipedia articles to explain and expand on the concepts. However, 32 | knowing the keyword is required to even know what to search for. 33 | 34 | ## Related Products 35 | * [Rust Programming By Example](https://www.packtpub.com/application-development/rust-programming-example?utm_source=github&utm_medium=repository&utm_campaign=9781788390637) 36 | 37 | * [Mastering Rust](https://www.packtpub.com/application-development/mastering-rust?utm_source=github&utm_medium=repository&utm_campaign=9781785885303) 38 | 39 | * [Rust Cookbook](https://www.packtpub.com/application-development/rust-cookbook?utm_source=github&utm_medium=repository&utm_campaign=9781785880254) 40 | 41 | 42 | ### Download a free PDF 43 | 44 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
45 |

https://packt.link/free-ebook/9781788839358

--------------------------------------------------------------------------------