├── .cargo └── config.toml ├── .gitignore ├── .idea ├── .gitignore ├── AP-class.iml ├── modules.xml └── vcs.xml ├── Cargo.lock ├── Cargo.toml ├── hello.txt └── src ├── classes ├── c01_basic.rs ├── c01_std.rs ├── c02_ownership.rs ├── c03_enums.rs ├── c04_structs.rs ├── c04_structshelper.rs ├── c05_modules.rs ├── c06_testing.rs ├── c07_generics.rs ├── c08_lifetimes.rs ├── c09_traits.rs ├── c10_OOP.rs ├── c11_heap.rs ├── c12_fp.rs ├── c13_maps.rs ├── c14_conc.rs ├── c99_QA.rs └── mod.rs ├── main.rs └── primers └── FP.md /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [registries] 2 | #kellnr = { index = "git://advancedprogramming.disi.unitn.it/index", token = ""} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/AP-class.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "AP-class" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "libtest", 10 | "rand", 11 | ] 12 | 13 | [[package]] 14 | name = "cfg-if" 15 | version = "1.0.0" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 18 | 19 | [[package]] 20 | name = "getrandom" 21 | version = "0.2.7" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" 24 | dependencies = [ 25 | "cfg-if", 26 | "libc", 27 | "wasi", 28 | ] 29 | 30 | [[package]] 31 | name = "leaflib" 32 | version = "0.1.0" 33 | 34 | [[package]] 35 | name = "libc" 36 | version = "0.2.126" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" 39 | 40 | [[package]] 41 | name = "libtest" 42 | version = "0.1.0" 43 | dependencies = [ 44 | "leaflib", 45 | ] 46 | 47 | [[package]] 48 | name = "ppv-lite86" 49 | version = "0.2.16" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 52 | 53 | [[package]] 54 | name = "rand" 55 | version = "0.8.5" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 58 | dependencies = [ 59 | "libc", 60 | "rand_chacha", 61 | "rand_core", 62 | ] 63 | 64 | [[package]] 65 | name = "rand_chacha" 66 | version = "0.3.1" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 69 | dependencies = [ 70 | "ppv-lite86", 71 | "rand_core", 72 | ] 73 | 74 | [[package]] 75 | name = "rand_core" 76 | version = "0.6.3" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 79 | dependencies = [ 80 | "getrandom", 81 | ] 82 | 83 | [[package]] 84 | name = "wasi" 85 | version = "0.11.0+wasi-snapshot-preview1" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 88 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "AP-class" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | # QUIZ 10 | libtest = { path = "../libtest" } # no need to include "libleaf" 11 | rand = "0.8.4" 12 | #piston_window = { version = "0.120.0", git = "https://github.com/PistonDevelopers/piston" } 13 | 14 | -------------------------------------------------------------------------------- /hello.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/squera/AP-class/d207e1182027911f0731269bf7e53b78839bb450/hello.txt -------------------------------------------------------------------------------- /src/classes/c01_basic.rs: -------------------------------------------------------------------------------- 1 | /// Material for this module: 2 | /// https://doc.rust-lang.org/book/ch03-00-common-programming-concepts.html 3 | /// https://doc.rust-lang.org/book/ch03-02-data-types.html 4 | /// https://doc.rust-lang.org/book/ch03-03-how-functions-work.html 5 | /// https://doc.rust-lang.org/book/ch03-05-control-flow.html 6 | 7 | use std::io; 8 | 9 | /// This function shows Rust variables, assignment and mutability 10 | pub fn var_ass_mut(){ 11 | let y : i32 = 0; 12 | let x = y +1 ; 13 | println!("Values of x and y: {} and {}", x, y); 14 | 15 | let x = 'c'; 16 | println!("Value of x: {}", x); 17 | // println!("Value of x: {}", x+1); 18 | // DNC: error[E0369]: cannot add `{integer}` to `&str` 19 | 20 | let y = 0; 21 | // y += 1; 22 | // DNC: error[E0384]: cannot assign twice to immutable variable `y` 23 | let z = 1; 24 | println!("Value of z: {}",z); 25 | println!("Value of z: {}",z+1); 26 | // Q: why is the line above ok? 27 | 28 | const _TRUE : i32 = 1; // _ for warning suppression 29 | } 30 | 31 | const _FALSE : i32 = 0; 32 | // QUIZ: can I use const FALSE from `src/main.rs` ? 33 | 34 | pub fn vals_types(){ 35 | // integers, usize and floats 36 | let x : i32 = 10; 37 | let y : i64 = 20; 38 | println!("Value of +: {}", x+(y as i32)); // explicit casting only 39 | let z : f32 = 1.2; 40 | let u : f64 = 3.45; 41 | println!("Value of +: {}", (z as f64)+u); 42 | 43 | // bools 44 | let t : bool = true; 45 | let f : bool = false; 46 | if t == f { 47 | println!("True is false"); 48 | } else { 49 | println!("True is not false"); 50 | } 51 | 52 | // chars 53 | let c = 'z'; 54 | let z = 'ℤ'; 55 | let heart_eyed_cat = '😻'; 56 | println!("Some chars: {}, {}, and {}", c, z, heart_eyed_cat); 57 | 58 | // tuples 59 | let t = (1,'c',false); 60 | let tt = t; 61 | // let last = t.; // uncomment and see RR's suggestions 62 | let (a,b,c) = t; 63 | println!("First element: {} = {} = {}", a,b,c); 64 | 65 | // arrays 66 | let _a: [i32; 5] = [1, 2, 3, 4, 5]; 67 | // Q: read this type to me 68 | // this initialises the array with length 5 to all values being 3 69 | let _a = [3; 5]; 70 | let a = [3, 3, 3, 3, 3]; 71 | let a1 = a[0]; 72 | let a2 = a[1]; 73 | println!("Array Elements: {} and {}", a1, a2); 74 | 75 | // let a6 = a[7]; // tentative buffer overflow 76 | // DNC: error: this operation will panic at runtime 77 | print!("Input element index to lookup:"); 78 | let mut input_text = String::new(); 79 | io::stdin() 80 | .read_line(&mut input_text) 81 | .expect("failed to read from stdin"); 82 | 83 | let trimmed = input_text.trim(); 84 | match trimmed.parse::() { 85 | Ok(mut i) => { 86 | println!("Integer input: {}", i); 87 | // if i>4 { i = 4; } // comment and input 6 88 | let _element = a[(i as usize)]; 89 | println!("This will not print without the if"); 90 | } 91 | , 92 | Err(..) => println!("this was not an integer: {}", trimmed), 93 | }; 94 | 95 | let mut aa = [(1,2),(1,4)]; 96 | println!("Array1 {:?}",aa); 97 | aa = [aa[0],(4,5)]; 98 | println!("Array2 {:?}",aa); 99 | aa[1].0 = 3; 100 | println!("Array3 {:?}",aa); 101 | // QUIZ: cosa viene stampato qui? 102 | // [(1, 2), (1, 4)] || [(1, 2), (4, 5)] || [(1, 2), (3, 5)] || [(3, 2), (4, 5)] || [(1, 3), (4, 5)] 103 | } 104 | 105 | pub fn expressions(){ 106 | // Rust has if-then and if-then-else conditionals 107 | // Rust has different forms of iteration 108 | // - loops 109 | let mut c = 0; 110 | loop { 111 | println!("This will never stop"); 112 | c += 1; 113 | if c == 4 { 114 | break; // breaking is the only way to escape an unguarded loop 115 | } 116 | } 117 | // - while loops 118 | while c != 0 { 119 | println!("Cycle with while {}!", c); 120 | c -= 1; 121 | } 122 | 123 | for n in 1..51 { // or 1..=50 124 | if n % 15 == 0 { 125 | println!("fizzbuzz"); 126 | } else if n % 3 == 0 { 127 | println!("fizz"); 128 | } else if n % 5 == 0 { 129 | println!("buzz"); 130 | } else { 131 | println!("{}", n); 132 | } 133 | } 134 | 135 | // array iteration 136 | let mut a = [10, 20, 30, 40, 50]; 137 | for element in a.iter() { // iter, iter_mut, into_iter 138 | println!("Iteration loop: the value is: {}", element); 139 | } 140 | } 141 | 142 | #[cfg(test)] 143 | pub mod testing { 144 | use super::testfuns::{crapadd, okadd}; 145 | 146 | // all functions marked as #[test] can be run with project testing 147 | #[test] 148 | fn test_crapadd() { 149 | println!("Testing Crap Add"); 150 | assert_eq!(crapadd(1,3),2); 151 | 152 | } 153 | #[test] 154 | fn test_okadd(){ 155 | println!("Testing OK Add"); 156 | assert_eq!(okadd(1, 5), 6); 157 | } 158 | } 159 | pub mod testfuns{ 160 | // statements VS expressions 161 | pub fn crapadd(x: i32,_y: i32) -> i32 { 162 | return x+x; 163 | } 164 | pub fn okadd(x: i32, y:i32) -> i32 { 165 | x+y 166 | } 167 | } 168 | 169 | -------------------------------------------------------------------------------- /src/classes/c01_std.rs: -------------------------------------------------------------------------------- 1 | /// https://doc.rust-lang.org/std/string/struct.String.html 2 | pub fn strings(){ 3 | let str1 : &str = "wat"; 4 | let mut str2 = "wht"; 5 | let s:String = String::from("asd"); 6 | str2 = "mehs"; 7 | println!("Strings {} and {}", str1 , str2); 8 | // what can one do with a &str and with a String? 9 | // str_string. 10 | // s. 11 | 12 | let str_string = String::from(str1); // owned string 13 | // Q: what tells us that `strptr` is a reference? 14 | let strptr_string : &String = &str_string; // ref to string (borrow) 15 | /* Strings ,visually: 16 | strptr_string str_string heap stuff 17 | | name | value | | name | value | | index | value | 18 | | ptr | ----------------> ptr | ---------------> 0 | h | 19 | | length | 5 | | 1 | e | 20 | | capacity | 5 | | 2 | l | 21 | | 3 | l | 22 | | 4 | o | 23 | */ 24 | let str_slice : &str = &str_string[..2]; 25 | println!("This is a slice {}", str_slice); 26 | println!("This is (not) a pointer: {}", strptr_string); 27 | println!("This is a pointer: {:p}", strptr_string); 28 | 29 | let s = "hell"; 30 | // Q: what is the type of _s? (erase _ to reveal type inference) 31 | let s0 = & "hell".to_string(); 32 | // s0.push('a'); // decomment and inspect the signature of 'push' 33 | // Q: why does this does not typecheck ? 34 | 35 | let mut s = "hell".to_string(); 36 | let t = String::from("o world"); 37 | s.push_str(&t); 38 | // inspect the signature of 'push_str' 39 | 40 | // Another way of manipulating strings is with `format!` 41 | let r = format!("{} is real t{}lings", s0, t); 42 | println!("{}",r); 43 | 44 | // let h = s0[0]; // Strings are non-indexable arrays 45 | // DNC: the type `String` cannot be indexed by `{integer}` 46 | for c in s0.chars() { 47 | println!("Char: {:?}", c) 48 | } 49 | } 50 | 51 | /// https://doc.rust-lang.org/std/vec/struct.Vec.html 52 | pub fn vec(){ 53 | let mut v: Vec = Vec::new(); 54 | v.push(5); 55 | v.push(5); 56 | v.push(6); 57 | v.push(5); 58 | v.pop(); 59 | let o = v.get(1); // check this type after 'get' 60 | let n = v.get(0).unwrap(); // then this type after 'unwrap' 61 | let mut nn = v.get(2).unwrap(); 62 | 63 | // QUIZ: can i do this: 64 | let nn = (nn + n); 65 | // nn = nn3; 66 | 67 | 68 | // let nn = *nn; //shadowing! 69 | println!("Adding stuff {}", nn + n); 70 | 71 | let mut vv : Vec = Vec::new(); 72 | vv.push("asd".to_string()); 73 | let mut x = vv.get_mut(0).unwrap(); //notice 'get_mut' instead of 'get' 74 | let xx = x.push('a'); 75 | println!("{}--",x); 76 | 77 | // immutable iteration 78 | for i in &v { 79 | println!("{}", i); 80 | } 81 | // mutable iteration 82 | for i in &mut v { 83 | *i += 50; 84 | } 85 | println!("Vector v {:?}", v); // print Debug gable stuff 86 | } 87 | 88 | pub fn mutability(){ 89 | // Q: what can you do on something of type pointer? 90 | 91 | let x : &mut String = &mut String::from("asd"); 92 | // Q: what is the type of 'x'? 93 | // x = x; 94 | // Q: can i decomment this? 95 | 96 | let mut z : &String = & String::from("ad"); 97 | z = z; 98 | // z.push('a'); 99 | // Q: can i decomment this? 100 | 101 | let mut y : &mut String = &mut String::from("asdasd"); 102 | y.push('a'); // fine: y points to a mutable String 103 | y = x; // also fine: y itself is mutable 104 | 105 | } 106 | 107 | /// https://doc.rust-lang.org/std/collections/struct.HashMap.html 108 | pub fn hashmap(){ 109 | use std::collections::HashMap; 110 | 111 | let mut scores = HashMap::new(); 112 | scores.insert(String::from("Blue"), 10); 113 | scores.insert(String::from("Yellow"), 50); 114 | for (key, value) in &scores { 115 | println!("Hashmap content: {}: {}", key, value); 116 | } 117 | //overwrite 118 | scores.insert(String::from("Blue"), 25); 119 | //get or insert in two different ways 120 | // with `get` and the related handling of options 121 | let blue_scores = scores.get("Blue").unwrap(); 122 | println!("blue: {}", blue_scores); 123 | } -------------------------------------------------------------------------------- /src/classes/c02_ownership.rs: -------------------------------------------------------------------------------- 1 | /// https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html 2 | pub fn ownership(){ 3 | // allocates s1 4 | let s1 = String::from("hello"); 5 | { // a new scope block 6 | let _s2 = s1;// moves s1 into s2 7 | // decomment the print below 8 | // println!("{}", s1); // error, `s1` doesn't own the value anymore. 9 | } 10 | // Q: what happens when I decomment this line? 11 | // println!("{}", s1); 12 | 13 | { // If we really want to keep `s1` and `s2` at the same time, 14 | // we can `clone` it explicitly: 15 | let s1 = String::from("hello"); 16 | let s2 = s1.clone(); 17 | 18 | println!("s1 = {}, s2 = {}", s1, s2); 19 | } 20 | // ownership for Copy types 21 | let x: i32 = 5; 22 | { 23 | let y = x; 24 | println!("x = {}, y = {}", x, y); //works 25 | } 26 | } 27 | 28 | // Q: when is the memory for the heap-allocated `s` freed ? 29 | pub fn ownership_for_functions() { 30 | let s = String::from("hello"); 31 | takes_ownership( s ); 32 | // Q: is s live here? 33 | let x = 5; 34 | // 'x' is Copy, so it is copied, not moved 35 | makes_copy(x); 36 | // 37 | } 38 | 39 | fn takes_ownership(some_string: String) { 40 | println!("{}", some_string); 41 | } 42 | 43 | fn makes_copy(some_integer: i32) { 44 | println!("{}", some_integer); 45 | } 46 | 47 | /// https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html 48 | pub fn refs_and_borrowing(){ 49 | let mut s1 = String::from("hello"); 50 | let len = calculate_length(&s1); // note the '&' 51 | println!("The length of '{}' is {}.", s1, len); 52 | // QUIZ: can i uncomment: 53 | // let len = (&s1).push('a'); 54 | 55 | let mut s = String::from("hello"); 56 | change(&mut s); 57 | 58 | let r1 = &mut s; 59 | // QUIZ: does this code compile? can i uncomment this? 60 | // let r2 = &mut s; 61 | let r2 = "asd"; 62 | println!("r1 and r2: {} and {}", r1, r2); 63 | 64 | let r1 = &s; 65 | let r2 = &s; 66 | let r3 = &s; 67 | println!("r1 and r2 and r3: {} and {} and {}", r1, r2, r3); 68 | // QUIZ: does this code compile? can we uncomment this line? 69 | let r3 = &mut s; 70 | // let x = s; 71 | println!("r3: {} ", r3); 72 | // println!("{}",x); 73 | // 74 | // println!("r1 and r2 and r3: {} and {} and {}", r1, r2, r3); 75 | 76 | // see dangle 77 | 78 | let string = no_dangle(); 79 | println!("String {}",string); 80 | } 81 | 82 | /// Example function used for borrowing 83 | fn calculate_length(s: &String) -> usize { 84 | s.len() 85 | } 86 | 87 | /// Example function used for borrowing 88 | fn change(some_string: &mut String) { 89 | some_string.push_str(", world"); 90 | } 91 | 92 | // fn dangle() -> &'static String { 93 | // let s = String::from("hello"); 94 | // &s 95 | // } 96 | 97 | fn no_dangle() -> String { 98 | let s = String::from("hello"); 99 | s // return s; // equivalent 100 | } 101 | 102 | /// https://doc.rust-lang.org/book/ch04-03-slices.html 103 | pub fn slices(){ 104 | let s : String = String::from("hello world"); 105 | let _hello : &str = &s[0..5]; 106 | let _world : &str = &s[6..11]; 107 | 108 | let _slice = &s[0..2]; // these are equal 109 | let _slice = &s[..2]; 110 | 111 | let len = s.len(); // these are equal too 112 | let _slice = &s[3..len]; 113 | let _slice = &s[3..]; 114 | 115 | let a = [1, 2, 3, 4, 5]; 116 | // let slice1 = &a[0..7]; 117 | let slice = &a[1..3]; 118 | assert_eq!(slice, &[2, 3]); 119 | 120 | let v = vec![1, 2, 3, 4, 5]; 121 | let v = &v[1..4]; 122 | let third: &i32 = &v[2]; 123 | println!("The third element is {}", third); 124 | match v.get(2) { 125 | Some(third) => println!("The third element is {}", third), 126 | None => println!("There is no third element."), 127 | } 128 | } 129 | 130 | 131 | pub fn ownership_and_compound(){ 132 | let mut v = vec![String::from("something");10]; 133 | // QUIZ: can i uncomment both lines? 134 | // let first_nonmut = v[0]; 135 | // let sec_nonmut = v[1]; 136 | 137 | let first_nonnmut = &v[0]; 138 | // note that this now is a &String 139 | 140 | // check the types! 141 | let mut first_mut = v.get_mut(0).unwrap(); 142 | first_mut.push_str(" else"); 143 | println!("First Element: {}",first_mut); 144 | 145 | let second_nonnmut = v.get(1).unwrap(); 146 | // QUIZ: what happens if we write 147 | // println!("First Element: {}",first_mut); 148 | // nothing: it works // compiler error 149 | 150 | // let cheat = &mut first_mut; //DNC 151 | let third_nonmut = v.get(2).unwrap(); 152 | println!("Nonmut: second and third {}, {}", second_nonnmut, third_nonmut); 153 | 154 | let (v05, v6plus) = v.split_at_mut(6); 155 | let mut one_mut = v05.get_mut(5).unwrap(); 156 | one_mut.push('a'); 157 | let mut two_mut = v6plus.get_mut(0).unwrap(); 158 | println!("5: {}, 6: {}",one_mut, two_mut); 159 | 160 | let mut vv = [v05,v6plus].concat(); 161 | println!("{:?} == {:?}",v,vv); 162 | 163 | // if (true ){ 164 | // return 5; 165 | // }else{ 166 | // return 'a'; 167 | // } 168 | } 169 | -------------------------------------------------------------------------------- /src/classes/c03_enums.rs: -------------------------------------------------------------------------------- 1 | pub enum IpAddrKind { 2 | V4, 3 | V6, 4 | } 5 | enum IpAddr { 6 | V4(i32,i32,i32,i32), 7 | V6(String), 8 | V0(), 9 | } 10 | 11 | 12 | /// https://doc.rust-lang.org/book/ch06-00-enums.html 13 | pub fn enum_usage(){ 14 | let _four = IpAddrKind::V4; 15 | let _six = IpAddrKind::V6; 16 | 17 | let loopback = IpAddr::V6(String::from("::1")); 18 | let home = IpAddr::V4(127, 0, 0, 1); 19 | } 20 | 21 | 22 | // https://doc.rust-lang.org/std/option/enum.Option.html 23 | /* enum Option { 24 | None, 25 | Some(T), 26 | } */ 27 | 28 | /// https://doc.rust-lang.org/std/option/enum.Option.html 29 | pub fn option(){ 30 | let x: i8 = 5; 31 | let y: Option = Some(5); // None 32 | // QUIZ: can i do: 33 | // let sum = x + y; 34 | 35 | let nopt : Option = None; 36 | let opt = Some(10); 37 | 38 | // QUIZ: what will these expressions do? 39 | // let xxv = nopt.unwrap(); 40 | // let v = opt.unwrap(); 41 | // println!("Some of {}",None); 42 | 43 | } 44 | 45 | /// https://doc.rust-lang.org/book/ch18-00-patterns.html?highlight=pattern%20ma#patterns-and-matching 46 | pub fn patternmatching(){ 47 | let home = IpAddr::V4(127, 0, 0, 1); 48 | let loopback = IpAddr::V6(String::from("::1")); 49 | // QUIZ: is this ok? 50 | // match home { 51 | // IpAddr::V4(a, b, c, d) => println!("Is V4"), 52 | // IpAddr::V6(a) => println!("Is V6") 53 | // }; 54 | match home { 55 | IpAddr::V4(127, _, c, d) => println!("Is V4 loopback"), 56 | IpAddr::V4(a, b, c, d) => println!("Is V4"), 57 | IpAddr::V6(a) => println!("Is V6"), 58 | IpAddr::V0() => println!("what") 59 | // _ => println!(" errror") 60 | }; 61 | let _variable = match loopback { 62 | IpAddr::V4(127, b, c, d) => Some(b), 63 | _ => None 64 | }; 65 | // Q : what is the type of `_variable` ? 66 | let firstfield = match IpAddr::V4(10,20,30,40){ 67 | IpAddr::V4(a,_,_,_) => a, 68 | _ => 0, 69 | }; 70 | println!("The first field is: {}", firstfield); 71 | 72 | let nopt : Option = None; 73 | let opt : Option = Some(3); 74 | let test_eq = match (opt, nopt) { 75 | (Some(o),Some(n)) => {o == n}, 76 | (Some(_),None) => {false}, 77 | (None,Some(_)) => {false}, 78 | (None, None) => {false}, 79 | // _ => true 80 | }; 81 | println!("Are they the same? {}", test_eq); 82 | 83 | let issome = nopt.is_some(); 84 | let isnone = opt.is_none(); 85 | let content = opt.unwrap(); 86 | let exp = opt.expect("insert error message here"); 87 | 88 | // iflet! 89 | let optional = Some(7); 90 | match optional { 91 | Some(i) => println!("This is a really long string and `{:?}`", i), 92 | _ => {}, 93 | // ^ Required because `match` is exhaustive. Doesn't it seem 94 | // like wasted space? 95 | }; 96 | if let Some(i) = optional { 97 | println!("Matched {:?}!", i); 98 | } 99 | let home = IpAddr::V4(127, 0, 0, 1); 100 | if let IpAddr::V0() = home { 101 | println!("home") 102 | } 103 | } 104 | 105 | 106 | /// https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html 107 | pub fn errors() { 108 | /* enum Result { 109 | Ok(T), 110 | Err(E), 111 | } */ 112 | use std::fs::File; 113 | 114 | // erase file and comment line to see panic 115 | File::create("hello.txt"); 116 | 117 | let f = File::open("hello.txt"); 118 | let f = match f { 119 | Ok(file) => file, 120 | Err(error) => panic!("Problem opening the file: {:?}", error), 121 | }; 122 | 123 | use std::io::ErrorKind; 124 | let f = match File::open("hello.txt") { 125 | Ok(file) => file, 126 | Err(error) => match error.kind() { 127 | ErrorKind::NotFound => match File::create("hello.txt") { 128 | Ok(fc) => fc, 129 | Err(e) => panic!("Problem creating the file: {:?}", e), 130 | }, 131 | other_error => { 132 | panic!("Problem opening the file: {:?}", other_error) 133 | } 134 | }, 135 | }; 136 | } 137 | 138 | 139 | 140 | fn qm() -> Option { 141 | let r1 = retop()?; // retop : void -> Option 142 | // Q: what happens if i remove the `?` ? 143 | let r = match retop(){ 144 | Some(z) => z, 145 | None => return None, 146 | }; 147 | return Some(r.len() as i32); 148 | } 149 | pub fn testqm() { 150 | let r = qm(); 151 | println!("Received {:?}",r); 152 | let r = retop(); 153 | println!("Received {:?}",r); 154 | } 155 | fn retop() -> Option{ 156 | return Some(String::from("asd")); 157 | } 158 | fn retn() -> Option { 159 | return None; 160 | } 161 | 162 | 163 | // -- --- -- -- 164 | 165 | use std::fs::File; 166 | use std::io::{Read, Write}; 167 | use std::io::prelude::*; 168 | 169 | pub fn readfilecontent () -> Result<(),String>{ 170 | 171 | // create a new file X -> deal with the Result 172 | let file = File::open("foo.txt"); 173 | let mut f = match file { 174 | Ok(x) => x, 175 | Err(e) => return Err(String::from("could not open")), 176 | }; 177 | let r = f.write_all(b"adv prog sssss"); 178 | // do not unwrap immediately -> issues when unwrapping later 179 | // write to it using write_all and a 'b' buffer -> deal with the Result 180 | // read its content 181 | let mut s = String::new(); 182 | f.read_to_string(&mut s); 183 | let scount = calculateS(&s); 184 | println!("String : {}, count: {}", s, scount); 185 | // feed to calculateS, with String parameter (not &String) -> Ownership! 186 | // printout the content and the s's 187 | return Ok(()); 188 | } 189 | // write out calculateS 190 | // use chars iterator 191 | // use eq_ignore_ascii_case 192 | fn calculateS(string : &String) -> i32{ 193 | let mut count =0; 194 | for x in string.chars(){ 195 | if x.eq_ignore_ascii_case(&'s'){ 196 | count +=1; 197 | } 198 | } 199 | return count; 200 | } 201 | -------------------------------------------------------------------------------- /src/classes/c04_structs.rs: -------------------------------------------------------------------------------- 1 | // https://doc.rust-lang.org/book/ch05-00-structs.html 2 | struct User { 3 | pub username: String, 4 | email: String, 5 | sign_in_count: u64, 6 | active: bool, 7 | } 8 | 9 | pub fn struct_usage(){ 10 | let _user0 = User { 11 | email: String::from("someone@example.com"), 12 | username: String::from("someusername123"), 13 | active: true, 14 | sign_in_count: 1, 15 | }; // Q: can i uncomment the line below ? 16 | // _user0.email = String::new(); 17 | let mut user1 = User { 18 | email: String::from("someone@example.com"), 19 | username: String::from("someusername123"), 20 | active: true, 21 | sign_in_count: 1, 22 | }; 23 | user1.email = String::new(); 24 | user1.username = String::new(); 25 | 26 | // QUIZ: if i go into a different file `c04_structshelper`, 27 | // can i write 28 | // let x = user1.email; 29 | 30 | let email = String::from("someone@example.com"); 31 | let username = String::from("someusername123"); 32 | 33 | let _user2 = User { 34 | email, 35 | username, 36 | active: true, 37 | sign_in_count: 1, 38 | }; 39 | 40 | let user3 = User { 41 | email: String::from("someone@example.com"), 42 | username: String::from("someusername123"), 43 | active: true, 44 | sign_in_count: 1, 45 | }; 46 | 47 | let _user4 = User { 48 | email: String::from("another@example.com"), 49 | username: String::from("anotherusername567"), 50 | ..user3 51 | }; 52 | let _user5 = User { 53 | email: String::from("another@example.com"), 54 | username: String::from("anotherusername567"), 55 | active: user1.active, 56 | sign_in_count: user1.sign_in_count, 57 | }; 58 | } 59 | 60 | pub fn retu() -> User{ 61 | let _user5 = User { 62 | email: String::from("another@example.com"), 63 | username: String::from("anotherusername567"), 64 | active: false, 65 | sign_in_count: 10, 66 | }; 67 | return _user5; 68 | } 69 | 70 | 71 | #[derive(Debug)] 72 | struct Rectangle { 73 | width: u32, 74 | height: u32, 75 | } // Usage: println!("{:?}",r); 76 | 77 | use std::fmt; 78 | 79 | /// https://doc.rust-lang.org/rust-by-example/hello/print/print_display.html 80 | impl fmt::Display for Square { 81 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 82 | write!(f, "{}x{}", self.side,self.side) 83 | } 84 | } 85 | 86 | pub fn struct_printing() { 87 | let rect = Rectangle{ width: 10, height: 20 }; 88 | let squa = Square{side: 10}; 89 | // println!("Printing a rectangle {}", rect); 90 | println!("Printing a rectangle {:?}", rect); // use :? 91 | println!("Printing a square {}", squa); 92 | } 93 | 94 | 95 | pub struct Square { 96 | side: u32 97 | } 98 | pub struct Rhombus { 99 | pub side: u32, 100 | acute_angle: i32, 101 | } 102 | pub fn new_rhombus() -> Rhombus{ 103 | return Rhombus{ side: 0, acute_angle: 0 }; 104 | } 105 | pub fn _new_square() -> Square{ 106 | return Square{ side: 0 }; 107 | } 108 | 109 | // GOTO structshelper file 110 | 111 | 112 | 113 | impl Rectangle { 114 | fn area(&self) -> u32 { 115 | self.width * self.height 116 | } 117 | pub fn perimeter(& self) -> u32 { 118 | return self.height * 2 + self.width * 2; 119 | } 120 | fn double(&mut self) { 121 | self.width = self.width * 2; 122 | self.height = self.height * 2; 123 | } 124 | fn take_ownership(self) { } 125 | // QUIZ: are these methods or functions: 126 | // 1 pub fn test1(&self) ... 127 | // 2 fn test2(&self, arg: int) ... 128 | // 3 fn test3(arg : int) ... 129 | } 130 | 131 | impl Rectangle { 132 | fn square(size: u32) -> Rectangle { 133 | Rectangle { 134 | width: size, 135 | height: size, 136 | } 137 | } 138 | pub fn new() -> Rectangle { 139 | Rectangle{ width: 10, height: 20 } 140 | } 141 | // pub fn new( width : u32, height : u32) -> Rectangle { 142 | // Rectangle{ width, height } 143 | // } 144 | pub fn new_with_params( width : u32, height : u32) -> Rectangle { 145 | Rectangle{ width, height } 146 | } 147 | } 148 | 149 | pub fn struct_impl(){ 150 | let mut r = Rectangle::new(); 151 | let a = r.area(); 152 | r.double(); 153 | let p = Rectangle::perimeter(&r); 154 | println!("The Rectangle is {:?}",r); 155 | println!("Area: {} and Perimeter: {}", a, p); 156 | } 157 | 158 | 159 | // 160 | 161 | struct Test { 162 | pub f: i32, 163 | pub s: Vec, 164 | } 165 | pub fn ownstructs() { 166 | let mut example = Test { 167 | f: 32, 168 | s: vec![1], 169 | }; 170 | let new_f = example.f; 171 | let new_s = example.s; 172 | println!("First {}", new_f); 173 | println!("Second {:?}",new_s); // who owns the vector of 1s? 174 | //Q: can i uncomment below? 175 | // println!("vec {:?}",example.s); 176 | // println!("Second {:?}",new_s); 177 | } 178 | -------------------------------------------------------------------------------- /src/classes/c04_structshelper.rs: -------------------------------------------------------------------------------- 1 | // accessibility modifiers across modules 2 | 3 | // use crate::classes::c04_structs::User; 4 | // use crate::full_files::c04_structs::Rectangle; 5 | // DNC: Struct `Rectangle` is private [E0603] 6 | use crate::classes::c04_structs::{_new_square, new_rhombus}; 7 | use crate::classes::c04_structs::Square; 8 | use crate::classes::c04_structs::Rhombus; 9 | use crate::classes::c04_structs::retu; 10 | pub fn _showcase_access () { 11 | // Q: can i uncomment? 12 | // let a = retu(); 13 | // println!("{}",a.username); 14 | 15 | // Q: can i uncomment? 16 | let s : Square = _new_square(); 17 | // Square{ 18 | // side : 01, 19 | // }; 20 | 21 | // QUIZ: can i write the following: 22 | // let rr = Rhombus{ side: 0, acute_angle: 0 }; 23 | 24 | let rr = new_rhombus(); 25 | let _x = rr.side; 26 | // rr.side = 10; 27 | 28 | // let _a = rr.acute_angle; 29 | } -------------------------------------------------------------------------------- /src/classes/c05_modules.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Formatter}; 2 | /// https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html 3 | 4 | // imports, see Crate.toml 5 | use libtest::toplevel_fun; 6 | use libtest::pubmod::pubmodfun; 7 | // local alias 8 | use libtest::PubEnum as PE; 9 | 10 | pub fn externalcall(){ 11 | // let us now try to use the imported functions 12 | let s = toplevel_fun(); 13 | let ss = pubmodfun(); 14 | println!("Got {} and {}", s, ss); 15 | 16 | // we can also declare something of the imported Enum 17 | let _en = PE::P1; 18 | // println!("Enum {:?}",_en); 19 | 20 | use rand::random; 21 | let x: u8 = random(); 22 | println!("Random u8: {}", x); 23 | } 24 | -------------------------------------------------------------------------------- /src/classes/c06_testing.rs: -------------------------------------------------------------------------------- 1 | // open the other project `libtest` 2 | // 3 | // There, you'll find a TEST 4 | -------------------------------------------------------------------------------- /src/classes/c07_generics.rs: -------------------------------------------------------------------------------- 1 | // this is a struct with generic fields 2 | struct Point { 3 | x: T, 4 | y: U, 5 | } 6 | //Q? what is T? 7 | // No type inspection! -> no instanceof! 8 | // impl Point{ 9 | // // pub fn add(e : T) -> T { 10 | // // if T == i32 { 11 | // // ... 12 | // // } 13 | // // if T == String{ 14 | // // ... 15 | // // } 16 | // // } 17 | // } 18 | impl Point { 19 | fn x(&self) -> &T { 20 | &self.x 21 | } 22 | 23 | } 24 | 25 | 26 | // this instead is an impl that only exists for points i32, i32. 27 | impl Point{ 28 | fn xx(&self) -> i32 { 29 | 0 30 | } 31 | } 32 | impl Point{ 33 | fn xx(&self) -> i32 { 34 | 1 35 | } 36 | } 37 | impl Point{ 38 | fn xxx(&self) -> i32 { 39 | 0 40 | } 41 | } 42 | 43 | pub fn struct_generic(){ 44 | let both_integer : Point = Point { x: 5, y: 10 }; 45 | let both_float : Point = Point { x: 1.0, y: 4.0 }; 46 | let integer_and_float : Point = Point { x: 5, y: 4.0 }; 47 | let val1:i32 = *both_integer.x(); 48 | let val2:i32 = integer_and_float.xxx(); 49 | 50 | // QUIZ: can i call this: 51 | // both_integer.xxx(); 52 | // both_float.xx(); 53 | 54 | println!("val1 and val2: {} and {}", val1, val2); 55 | } 56 | 57 | pub fn explicit_type(){ 58 | let x = Some(true); 59 | // let x = None; 60 | println!("{:?}",x); 61 | let b = String::new(); 62 | let trimmed = b.trim(); 63 | // turbofish! 64 | match trimmed.parse::() { 65 | _ => () 66 | } 67 | } 68 | 69 | /* 70 | // This function 71 | fn main() { 72 | let integer = Some(5); 73 | let float = Some(5.0); 74 | } 75 | 76 | // Gets compiled by the rust compiler into 77 | enum Option_i32 { 78 | Some(i32), 79 | None, 80 | } 81 | enum Option_f64 { 82 | Some(f64), 83 | None, 84 | } 85 | fn main() { 86 | let integer = Option_i32::Some(5); 87 | let float = Option_f64::Some(5.0); 88 | } 89 | */ 90 | -------------------------------------------------------------------------------- /src/classes/c08_lifetimes.rs: -------------------------------------------------------------------------------- 1 | /// https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html 2 | 3 | // struct User { 4 | // username: & str, 5 | // email: & str, 6 | // sign_in_count: u64, 7 | // active: bool, 8 | // } 9 | struct User2 { 10 | username: &'static str, 11 | email: &'static str, 12 | sign_in_count: u64, 13 | active: bool, 14 | } 15 | #[derive(Debug)] 16 | struct IntUser<'a> { 17 | username: &'a i32, 18 | email: &'a i32, 19 | sign_in_count: u64, 20 | active: bool, 21 | } 22 | 23 | // 24 | pub fn lifetime_test() { 25 | let user1 = User2 { 26 | email: "someone@example.com", 27 | username: "someusername123", 28 | active: true, 29 | sign_in_count: 1, 30 | }; 31 | let user2 = User2 { 32 | email: "someone@example.com", 33 | username: "someusername123", 34 | active: true, 35 | sign_in_count: 1, 36 | }; 37 | // // QUIZ: does this code respect lifetimes? 38 | // { 39 | // let r: i32 = 0; // ---------+-- 'a 40 | // // | 41 | // { // | 42 | // let mut x : &i32 = &5; // -+-- 'b | 43 | // x = &r; // | | 44 | // println!("{}",x); // | | 45 | // } // -+ | 46 | // // | 47 | // println!("r: {}", r); // | 48 | // } // ---------+ 49 | // // QUIZ: does this code respect lifetimes? 50 | // { 51 | // let r; // ---------+-- 'a 52 | // // | 53 | // { // | 54 | // let x = 5; // -+-- 'b | 55 | // r = &x; // | | 56 | // } // -+ | 57 | // // | 58 | // println!("r: {}", r); // | 59 | // } // ---------+ 60 | } 61 | 62 | 63 | 64 | // fn longest(x:&str, y:&str) -> &str { 65 | // if x.len() > y.len() { x } else { y} 66 | // } // this function doesn't work 67 | 68 | pub fn uselongest() { 69 | let x = String::from("hi"); 70 | let z; 71 | { 72 | let y = String::from("there"); 73 | // z = longest(&x,&y); 74 | z = correct_longest(&x,&y); //will be &y 75 | 76 | println!("z = {}",z); 77 | } //drop y, and thereby z 78 | // println!("z = {}", z); 79 | } 80 | 81 | fn correct_longest<'a>(x:&'a str, y:&'a str) -> &'a str { 82 | if x.len() > y.len() { x } else { y } 83 | } 84 | 85 | fn another_longest<'a,'b>(x:&'a str, y:&'b str) -> &'b str { 86 | // QUIZ: does this compile? 87 | // return if x.len() > y.len() { x } else { y }; 88 | return y; 89 | } 90 | 91 | // Q: // why is this ok? 92 | fn outliving_longest<'b, 'a: 'b>(x:&'a str, y:&'b str) -> &'b str { 93 | if x.len() > y.len() { x } else { y} 94 | } 95 | 96 | // FAQs 1, 2, 3 97 | // fn one(x: &str) -> &str{ // x : 'a -> 'a 98 | // x 99 | // } 100 | // fn selfone<'a>(&self, x:&'a str)-> &'a str{ 101 | // x 102 | // } 103 | 104 | pub fn testintuser(){ 105 | let i = 5; 106 | let j = 10; 107 | 108 | let user = IntUser { 109 | email: &i, 110 | username: &i, 111 | active: true, 112 | sign_in_count: 1, 113 | }; 114 | { 115 | let k = 20; 116 | 117 | let user2 = IntUser { //lifetime subsumption 118 | email: &i, 119 | username: &k, 120 | active: true, 121 | sign_in_count: 1, 122 | }; 123 | } 124 | // println!("{:?}",user2); 125 | } 126 | 127 | 128 | // http://blog.pnkfx.org/blog/2019/06/26/breaking-news-non-lexical-lifetimes-arrives-for-everyone 129 | fn nll() { // SCOPE TREE 130 | let mut names = // +- `names` scope start 131 | ["abe", "beth", "cory", "diane"]; // | 132 | // | 133 | let alias = &mut names[0]; // | +- `alias` scope start 134 | // | | 135 | *alias = "alex"; // <------------------------ write to `*alias` 136 | // | | 137 | println!("{}", names[0]); // <--------------- read of `names[0]` 138 | // | | 139 | // | +- `alias` scope end 140 | // +- `name` scope end 141 | } 142 | pub fn nll_example() { 143 | let mut s = String::from("hello"); 144 | let r1 = &s; 145 | let r2 = &s; 146 | 147 | let r3 ; 148 | println!("{} and {}", r1, r2); 149 | r3 = &mut s; // r1 and r2 are no longer used after this point 150 | // QUIZ: can i uncomment this ? 151 | // println!("{}", r3); 152 | } 153 | 154 | 155 | // So how do we use references in struct definition? 156 | // we need lifetime annotations in structs 157 | struct Good_User<'a, 'b> { 158 | username: &'a str, 159 | email: &'b str, 160 | sign_in_count: u64, 161 | active: bool, 162 | } 163 | fn use_lifetimes() { 164 | let user1 = Good_User { 165 | email: "someone@example.com", 166 | username: "someusername123", 167 | active: true, 168 | sign_in_count: 1, 169 | }; 170 | } 171 | 172 | // this struct defines a lifetime parameter, 173 | // we can only instantiate it with a str that is already valid 174 | struct ImportantExcerpt<'a> { 175 | part: &'a str, 176 | } 177 | 178 | impl<'a> ImportantExcerpt<'a> { 179 | // QUIZ: do i need the lifetime annotation here on &self? 180 | fn level(&self) -> i32 { 181 | 3 182 | } 183 | // QUIZ: do i need the lifetime annotation here ? 184 | fn announce_and_return_part(&self, announcement: &str) -> &str { 185 | println!("Attention please: {}", announcement); 186 | self.part 187 | } 188 | fn announce_and_return_part_2(&self, announcement: &str) -> &str { 189 | println!("Attention please: {}", announcement); 190 | self.part 191 | } 192 | } 193 | 194 | struct UnimportantExcerpt<'a, 'b>{ 195 | part1: &'a str, 196 | part2: &'b str 197 | } 198 | impl<'a, 'b> UnimportantExcerpt<'a, 'b>{ 199 | fn testme(&self, param: &str) -> &str{ 200 | self.part2 201 | } 202 | } 203 | 204 | pub fn main() { 205 | let novel = String::from("Call me Ishmael. Some years ago..."); 206 | let first_sentence = novel.split('.').next().expect("Could not find a '.'"); 207 | 208 | // Q: do the lifetimes check out? 209 | let i = ImportantExcerpt { 210 | part: first_sentence, 211 | }; 212 | 213 | let mut second : &str = "asd"; 214 | // { // in this example, error[E0597]: `another` does not live long enough 215 | // let another = String::from("Call me Ishmael. Some years ago..."); 216 | // second = another.split('a').next().expect("what"); 217 | // } 218 | let ii = ImportantExcerpt{ 219 | part : second 220 | }; 221 | 222 | let x = i.announce_and_return_part_2("asd"); 223 | println!("{}A", x); 224 | } 225 | 226 | -------------------------------------------------------------------------------- /src/classes/c09_traits.rs: -------------------------------------------------------------------------------- 1 | fn the_large_one_i8(x: i8, y:i8) -> i8 {if x > y {x} else {y}} 2 | fn the_large_one_i16(x: i16, y:i16) -> i16 {if x > y {x} else {y}} 3 | 4 | // Q: why should this code not compile? 5 | // fn the_large_one_gen(x: T, y: T) -> T {if x > y {x} else {y}} 6 | 7 | fn the_large_one_gen_correct(x: T, y: T) -> T { 8 | if x > y {x} else {y} 9 | } 10 | 11 | pub trait Summary { 12 | fn summarize(&self) -> String; 13 | fn say_hello() where Self: Sized { 14 | println!("Hello") 15 | } 16 | } 17 | pub struct NewsArticle { 18 | pub headline: String, 19 | pub location: String, 20 | pub author: String, 21 | pub content: String, 22 | } 23 | // QUIZ: Will this compile: 24 | // impl Summary for NewsArticle { 25 | // } 26 | 27 | impl Summary for NewsArticle { 28 | fn summarize(&self) -> String { 29 | format!("{}, by {} ({})", self.headline, self.author, self.location) 30 | } 31 | } 32 | 33 | pub struct Tweet { 34 | pub username: String, 35 | pub content: String, 36 | pub reply: bool, 37 | pub retweet: bool, 38 | } 39 | 40 | impl Summary for Tweet { 41 | fn summarize(&self) -> String { 42 | format!("{}: {}", self.username, self.content) 43 | } 44 | fn say_hello() { 45 | println!("Yello!") 46 | } 47 | } 48 | 49 | pub fn traitexample(){ 50 | let t = Tweet{ 51 | username: "Marco".to_string(), 52 | content: "no way jose".to_string(), 53 | reply: false, 54 | retweet: false 55 | }; 56 | let n = NewsArticle{ 57 | headline: "Wasps!".to_string(), 58 | location: "Povo".to_string(), 59 | author: "Patrignani".to_string(), 60 | content: "there is a wasp in my attic".to_string() 61 | }; 62 | t.summarize(); 63 | n.summarize(); 64 | 65 | // QUIZ: what do these print? 66 | NewsArticle::say_hello(); 67 | Tweet::say_hello(); 68 | } 69 | 70 | 71 | fn notify_fn(item: &impl Summary) { 72 | println!("Breaking news! {}", item.summarize()); 73 | } 74 | 75 | fn notify_bound(item: &T) { 76 | println!("More Breaking news! {}", item.summarize()); 77 | } 78 | 79 | use std::cmp::Ordering; 80 | // let's import 2 commonly used traits first 81 | use std::fmt::{Debug, Display, Formatter}; 82 | 83 | fn notify_fn2(item1: &(impl Summary + Display), item2: &(impl Summary + Display)) {} 84 | 85 | fn notify_bound2(item1: &T, item2: &T) {} 86 | 87 | pub fn example_notify(){ 88 | let t = Tweet{ 89 | username: "Marco".to_string(), 90 | content: "no way jose".to_string(), 91 | reply: false, 92 | retweet: false 93 | }; 94 | notify_fn(&t); 95 | notify_bound(&t); 96 | // QUIZ: why will this not compile? 97 | notify_fn2(&t, &t); 98 | notify_bound2(&t, &t); 99 | 100 | let n = NewsArticle{ 101 | headline: "Wasps!".to_string(), 102 | location: "Povo".to_string(), 103 | author: "Patrignani".to_string(), 104 | content: "there is a wasp in my attic".to_string() 105 | }; 106 | notify_fn2(&t, &n); 107 | // notify_bound2(&t, &n); 108 | // 109 | // DNC: error[E0277]: `Tweet` doesn't implement `std::fmt::Display` 110 | } 111 | 112 | impl Display for Tweet { 113 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 114 | format!(""); 115 | Ok(()) 116 | } 117 | } 118 | 119 | impl Display for NewsArticle { 120 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 121 | format!(""); 122 | Ok(()) 123 | } 124 | } 125 | 126 | // pub trait Sum{ 127 | // fn summarize(&self) -> String; 128 | // } 129 | // impl Sum for Tweet { 130 | // fn summarize(&self) -> String { 131 | // todo!() 132 | // } 133 | // } 134 | 135 | // fn testx (param : &(impl Sum + Summary)) { 136 | // param.summarize(); 137 | // } 138 | 139 | 140 | fn some_function(t: &T, u: &U) -> i32 { 141 | 0 142 | } 143 | fn some_function_where(t: &T, u: &U) -> i32 144 | where T: Display + Clone, 145 | U: Clone + Debug{ 146 | 0 147 | } 148 | 149 | /* ==== Trait Objects ====== 150 | ====================== */ 151 | // fn ret_trait_wrong() -> dyn Summary { 152 | // let t = Tweet{ 153 | // username: "Marco".to_string(), 154 | // content: "no way jose".to_string(), 155 | // reply: false, 156 | // retweet: false 157 | // }; 158 | // let n = NewsArticle{ 159 | // headline: "Wasps!".to_string(), 160 | // location: "Povo".to_string(), 161 | // author: "Patrignani".to_string(), 162 | // content: "there is a wasp in my attic".to_string() 163 | // }; 164 | // if rand::random() < 0.5 { 165 | // t 166 | // } else { 167 | // n 168 | // } 169 | // } 170 | 171 | fn ret_trait() -> Box { 172 | let t = Tweet{ 173 | username: "Marco".to_string(), 174 | content: "no way jose".to_string(), 175 | reply: false, 176 | retweet: false 177 | }; 178 | let n = NewsArticle{ 179 | headline: "Wasps!".to_string(), 180 | location: "Povo".to_string(), 181 | author: "Patrignani".to_string(), 182 | content: "there is a wasp in my attic".to_string() 183 | }; 184 | if 0.1 < 0.5 { 185 | // and now we return stuff allocated in a Box 186 | Box::new(t) 187 | } else { 188 | Box::new(n) 189 | } 190 | } 191 | fn client(){ 192 | let x = ret_trait(); 193 | x.summarize(); 194 | } 195 | 196 | 197 | struct Sheep {} 198 | struct Cow {} 199 | 200 | trait Animal { 201 | fn noise(&self) -> &'static str; 202 | } 203 | 204 | 205 | impl Animal for Sheep { 206 | fn noise(&self) -> &'static str { 207 | "baaaaah!" 208 | } 209 | } 210 | 211 | impl Animal for Cow { 212 | fn noise(&self) -> &'static str { 213 | "moooooo!" 214 | } 215 | } 216 | // QUIZ: What's the return type? 217 | fn random_animal(random_number: f64) -> 218 | // 219 | Box { 220 | if random_number < 0.5 { 221 | Box::new(Sheep {}) 222 | } else { 223 | Box::new(Cow {}) 224 | } 225 | } 226 | 227 | pub fn animals_example() { 228 | let random_number = rand::random(); 229 | let animal = random_animal(random_number); 230 | println!("You've randomly chosen an animal, and it says {}", animal.noise()); 231 | } 232 | 233 | /* ==== Conditional Trait Implementation ====== 234 | ====================== */ 235 | struct Pair { 236 | x: T, 237 | y: T, 238 | } 239 | 240 | impl Pair { 241 | fn new(x: T, y: T) -> Self { 242 | Self { x, y } 243 | } 244 | fn print(&self) { 245 | println!("Pair") 246 | } 247 | } 248 | impl Pair { 249 | fn cmp_display(&self) { 250 | if self.x >= self.y { 251 | println!("The largest member is x = {}", self.x); 252 | } else { 253 | println!("The largest member is y = {}", self.y); 254 | } 255 | } 256 | } 257 | 258 | 259 | /* ==== Deriving Traits ==== 260 | ====================== */ 261 | 262 | #[derive(PartialEq, PartialOrd)] 263 | struct Centimeters(f64); 264 | #[derive(Debug,PartialEq)] 265 | struct Inches(i32); 266 | impl Inches { 267 | fn to_centimeters(&self) -> Centimeters { 268 | let &Inches(inches) = self; 269 | Centimeters(inches as f64 * 2.54) 270 | } 271 | } 272 | 273 | struct Seconds(i32); 274 | 275 | fn example_derivable() { 276 | let _one_second = Seconds(1); 277 | // QUIZ: why do this not compile? 278 | // println!("One second looks like: {:?}", _one_second); 279 | // QUIZ: why do this not compile? 280 | // let _this_is_true = (_one_second == _one_second); 281 | 282 | let foot = Inches(12); 283 | println!("One foot equals {:?}", foot); 284 | let meter = Centimeters(100.0); 285 | let cmp = 286 | if foot.to_centimeters() < meter { 287 | "smaller" 288 | } else { 289 | "bigger" 290 | }; 291 | 292 | println!("One foot is {} than one meter.", cmp); 293 | } 294 | // impl PartialOrd for Point{ 295 | // fn partial_cmp(&self, other: &Self) -> Option { 296 | // todo!() 297 | // } 298 | // } 299 | 300 | /* === Self (capital S) ==== 301 | ====================== */ 302 | trait T { 303 | type Item; 304 | const C: i32; 305 | fn new() -> Self; 306 | fn f(&self) -> Self::Item; 307 | } 308 | struct ST; 309 | impl T for ST { 310 | type Item = i32; 311 | const C: i32 = 9; 312 | fn new() -> Self { // `Self` is the type `ST`. 313 | ST 314 | } 315 | fn f(&self) -> Self::Item { // `Self::Item` is the type `i32`. 316 | Self::C // `Self::C` is the constant value `9`. 317 | } 318 | } 319 | pub fn test () { 320 | let st = ST::new(); 321 | let n = st.f(); 322 | } 323 | 324 | 325 | // pub fn testtt(a : Box>) -> ST{ 326 | // return 3; 327 | // } 328 | 329 | /* ===== Super Traits ====== 330 | ====================== */ 331 | 332 | trait Person { 333 | fn name(&self) -> &String; 334 | } 335 | trait Student: Person { 336 | fn university(&self) -> String; 337 | fn samesig(&self){} 338 | } 339 | trait Programmer { 340 | fn samesig(&self){} 341 | fn fav_language(&self) -> String; 342 | fn git_username(&self) -> String; 343 | } 344 | trait CompSciStudent: Programmer + Student { 345 | fn git_username(&self) -> String; 346 | } 347 | // Q: isn't this "multiple inheritance" ? 348 | 349 | fn comp_sci_student_greeting(student: &impl CompSciStudent) -> String { 350 | format!( 351 | "My name is {} and I attend {}. My favorite language is {}. My Git username is {}", 352 | student.name(), 353 | student.university(), 354 | student.fav_language(), 355 | Programmer::git_username(student) 356 | // student.git_username(), // Q: comment above, see error 357 | ) 358 | } 359 | 360 | struct Entity{ 361 | nm:String, 362 | } 363 | // QUIZ: how many IMPLs do we need now for 364 | // Entity if we say it implements CompSciStudent? 365 | // comment this impl, retype and see the issues 366 | 367 | 368 | impl CompSciStudent for Entity{ 369 | fn git_username(&self) -> String { 370 | String::from("squera") 371 | } 372 | } 373 | impl Programmer for Entity { 374 | fn fav_language(&self) -> String { 375 | String::from("Rust!") 376 | } 377 | fn git_username(&self) -> String { 378 | String::from("aswd") 379 | } 380 | } 381 | impl Student for Entity { 382 | fn university(&self) -> String { 383 | String::from("unitn") 384 | } 385 | } 386 | impl Person for Entity { 387 | fn name(&self) -> &String { 388 | &self.nm 389 | } 390 | } 391 | 392 | pub fn example_supertraits(){ 393 | let e = Entity{ 394 | nm : String::from("marco") 395 | }; 396 | let f = Entity{ 397 | nm : String::from("pigna") 398 | }; 399 | println!("{}",comp_sci_student_greeting(&e)); 400 | println!("{}",comp_sci_student_greeting(&f)); 401 | } 402 | 403 | 404 | 405 | 406 | /* ===== Polymorphism ====== 407 | ========================= */ 408 | 409 | trait Descrivibile { 410 | fn descrivi(&self) -> String; 411 | } 412 | 413 | fn stampa1(item: Box) { 414 | println!("{}", item.descrivi()); 415 | } 416 | 417 | fn stampa2(item: Box) { 418 | println!("{}", item.descrivi()); 419 | } 420 | 421 | fn stampa3(item: Box) { 422 | println!("{}", item.descrivi()); 423 | } -------------------------------------------------------------------------------- /src/classes/c10_OOP.rs: -------------------------------------------------------------------------------- 1 | /* ======= Objects ========= 2 | ====================== */ 3 | pub struct Rectangle { 4 | width: u32, 5 | height: u32, 6 | } 7 | 8 | impl Rectangle { 9 | pub fn new(width: u32, height: u32) -> Rectangle { 10 | Rectangle { 11 | width, 12 | height, 13 | } 14 | } 15 | } 16 | 17 | /* ===== Encapsulation ===== 18 | ====================== */ 19 | // QUIZ: public / private 20 | 21 | 22 | pub struct AveragedCollection { 23 | list: Vec, 24 | average: f64, 25 | } 26 | 27 | impl AveragedCollection { 28 | pub fn add(&mut self, value: i32) { 29 | self.list.push(value); 30 | self.update_average(); 31 | } 32 | 33 | pub fn get_average(&self) -> f64 { 34 | self.average 35 | } 36 | 37 | fn update_average(&mut self) { 38 | let total: i32 = self.list.iter().sum(); 39 | self.average = total as f64 / self.list.len() as f64; 40 | } 41 | } 42 | 43 | 44 | /* ===== Inheritance ======= 45 | ====================== */ 46 | 47 | //QUIZ: what Rust feature enables dynamic dispatch? 48 | 49 | fn wrong_Vecs() { 50 | let v1 = vec![1u32, 1u32]; 51 | let v2 = vec![1u64, 1u64]; 52 | // let v3 = vec![1u32, 1u64]; // DNC: error[E0308]: mismatched types 53 | } 54 | 55 | trait Show { 56 | fn show(&self) -> String; 57 | } 58 | impl Show for i32 { 59 | fn show(&self) -> String { 60 | format!("four-byte signed {}", self) 61 | } 62 | } 63 | impl Show for f64 { 64 | fn show(&self) -> String { 65 | format!("eight-byte float {}", self) 66 | } 67 | } 68 | impl Show for String{ 69 | fn show(&self) -> String{ 70 | self.clone() 71 | } 72 | } 73 | pub fn example_oop1() { 74 | let answer = 42; 75 | let maybe_pi = 3.14; 76 | let s = "what".to_string(); 77 | 78 | // QUIZ: does this compile? 79 | let v: Vec<&dyn Show> = vec![&answer, &maybe_pi, &s]; 80 | for d in v.iter() { 81 | println!("show {}", d.show()); 82 | } 83 | } 84 | 85 | 86 | trait Quack { 87 | fn quack(&self); 88 | } 89 | // Duck is a "class" 90 | struct Duck (); 91 | impl Quack for Duck { // this looks like Duck implements Quack , in Java 92 | fn quack(&self) { 93 | println!("quack!"); 94 | } 95 | } 96 | // second class 97 | struct RandomBird { 98 | is_a_parrot: bool 99 | } 100 | // this looks like RandomBird implements Quack , in Java 101 | impl Quack for RandomBird { 102 | fn quack(&self) { 103 | if ! self.is_a_parrot { 104 | println!("quack!"); 105 | } else { 106 | println!("squawk!"); 107 | } 108 | } 109 | } 110 | 111 | pub fn example_animals_oop() { 112 | let duck1 = Duck(); 113 | let duck2 = RandomBird { is_a_parrot: false }; 114 | let parrot = RandomBird { is_a_parrot: true }; 115 | 116 | let ducks: Vec<&dyn Quack> = vec![&duck1, &duck2, &parrot]; 117 | 118 | // QUIZ: what does this print ? 119 | // qqs | qss | qqq 120 | for d in &ducks { 121 | d.quack(); 122 | } 123 | 124 | println!("And now with subtleties:"); 125 | // these 2 pairs of functions 126 | // print the same things, but operate differently 127 | quack_ref(&duck1); 128 | quack_ref(&parrot); 129 | quack_trait(&duck1); 130 | quack_trait(&parrot); 131 | } 132 | // QUIZ: what is the difference between these two? 133 | fn quack_ref (q: &dyn Quack) { 134 | q.quack(); 135 | } 136 | fn quack_trait (q: &Q) 137 | where Q: Quack { 138 | q.quack(); 139 | } 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | // recall the trait Show from above, this is a new trait 157 | trait Location { 158 | fn location(&self) -> String; 159 | } 160 | trait ShowTell: Show + Location {} 161 | 162 | #[derive(Debug)] 163 | struct Foo { 164 | name: String, 165 | location: String 166 | } 167 | // some methods for allocating this struct 168 | impl Foo { 169 | fn new(name: &str, location: &str) -> Foo { 170 | Foo{ 171 | name: name.to_string(), 172 | location: location.to_string() 173 | } 174 | } 175 | } 176 | // let's make Foo implement Show 177 | impl Show for Foo { 178 | fn show(&self) -> String { 179 | self.name.clone() 180 | } 181 | } 182 | // and also Location 183 | impl Location for Foo { 184 | fn location(&self) -> String { 185 | self.location.clone() 186 | } 187 | } 188 | impl ShowTell for Foo {} 189 | 190 | // let's create 3 simple functions to tell the types 191 | fn transferShow(x: &dyn Show) -> &dyn Show { 192 | x 193 | } 194 | fn transferLocation(x: &dyn Location) -> &dyn Location { 195 | x 196 | } 197 | fn transferShowTell(x: &dyn ShowTell) -> &dyn ShowTell { 198 | x 199 | } 200 | 201 | pub fn example_multiple_traits(){ 202 | let f = Foo::new("n", "a"); 203 | // let's look at the types of these three variables 204 | let ff1 = transferShow(&f); 205 | let ff2 = transferLocation(&f); 206 | let ff3 = transferShowTell(&f); 207 | println!(" Foo's: {}, Show's: {}, Location's {}, ShowTell's: {}", f.name, ff1.show(), ff2.location(), ff3.show() ); 208 | } -------------------------------------------------------------------------------- /src/classes/c11_heap.rs: -------------------------------------------------------------------------------- 1 | /* ========== Box ========== 2 | ========================= */ 3 | pub fn example_box(){ 4 | let b = Box::new(5); 5 | println!("b's value = {}", *b); 6 | println!("b's address = {}", b); 7 | println!("b's real address = {:p}", b); 8 | println!("b's another address = {:p}", &b); 9 | } 10 | 11 | use std::mem; 12 | 13 | #[allow(dead_code)] 14 | #[derive(Debug, Clone, Copy)] 15 | struct Point { 16 | x: f64, 17 | y: f64, 18 | } 19 | #[allow(dead_code)] 20 | struct Rectangle { 21 | top_left: Point, 22 | bottom_right: Point, 23 | } 24 | fn origin() -> Point { 25 | Point { x: 0.0, y: 0.0 } 26 | } 27 | fn boxed_origin() -> Box { 28 | Box::new(Point { x: 0.0, y: 0.0 }) 29 | } 30 | 31 | pub fn example_box_long() { 32 | // Stack allocated variables 33 | let point: Point = origin(); 34 | let rectangle: Rectangle = Rectangle { 35 | top_left: origin(), 36 | bottom_right: Point { x: 3.0, y: -4.0 } 37 | }; 38 | // Heap allocated rectangle 39 | let boxed_rectangle: Box = Box::new(Rectangle { 40 | top_left: origin(), 41 | bottom_right: Point { x: 3.0, y: -4.0 }, 42 | }); 43 | 44 | // The output of functions can be boxed 45 | let boxed_point: Box = Box::new(origin()); 46 | 47 | // Double indirection: we can put a box in a box 48 | let box_in_a_box: Box> = Box::new(boxed_origin()); 49 | 50 | // let's now print a few sizes on the stack, to understand space consumption 51 | println!("Point occupies {} bytes on the stack", mem::size_of_val(&point)); 52 | println!("Rectangle occupies {} bytes on the stack", mem::size_of_val(&rectangle)); 53 | 54 | // QUIZ: 55 | // what's going to be printed next? 56 | // 8 | 16 | 32 57 | println!("Boxed point occupies {} bytes on the stack", mem::size_of_val(&boxed_point)); 58 | println!("Boxed rectangle occupies {} bytes on the stack", mem::size_of_val(&boxed_rectangle)); 59 | println!("Boxed box occupies {} bytes on the stack", mem::size_of_val(&box_in_a_box)); 60 | 61 | // Copy the data contained in `boxed_point` into `unboxed_point` 62 | let unboxed_point: Point = *boxed_point; 63 | println!("Unboxed point occupies {} bytes on the stack", mem::size_of_val(&unboxed_point)); 64 | } 65 | // Recursive types 66 | //QUIZ: 67 | // which of the following is a recursive type? 68 | // Array | List | Stream | Pair 69 | 70 | // uncomment, comment 71 | // enum WishList{ 72 | // WCons(i32, WishList), 73 | // WNil, 74 | // } 75 | // DNC: error[E0072]: recursive type `List` has infinite size 76 | 77 | enum List { 78 | Cons(i32, Box), 79 | Nil, 80 | } 81 | use self::List::{Cons,Nil}; 82 | pub fn recursivetypes(){ 83 | // let list : WishList = WCons(1, WCons(2, WCons(3, WNil))); 84 | let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); 85 | 86 | } 87 | 88 | 89 | /* ========= Deref ========= 90 | ========================= */ 91 | use std::ops::Deref; 92 | 93 | struct MyBox{ 94 | el : T, 95 | idx : i32 96 | } 97 | impl MyBox { 98 | fn new(x: T) -> MyBox { 99 | MyBox{el:x,idx:0} 100 | } 101 | } 102 | 103 | pub fn example_smart1() { 104 | // first, we test on the canonical Box 105 | let x = 5; 106 | let y = Box::new(x); 107 | 108 | println!("I expect 5: {}", x); 109 | println!("I expect 5: {}", *y); 110 | // Behind the scenes in this example, what is actually run is this. 111 | // (x.deref()) 112 | // *(y.deref()) 113 | 114 | let x = 5; 115 | let y = MyBox::new(x); 116 | 117 | // QUIZ: what does the second println print? 118 | println!("I expect 5: {}", x); 119 | // comment the IMPL below 120 | println!("I expect! 5: {}", *y); 121 | } 122 | 123 | // impl Deref for MyBox { 124 | // type Target = T; 125 | // // type Target = i32; 126 | // 127 | // fn deref(&self) -> &Self::Target { 128 | // &self.el 129 | // // &self.idx 130 | // } 131 | // } 132 | 133 | 134 | /* ========== Drop ========= 135 | ========================= */ 136 | struct CustomSmartPointer { 137 | data: String, 138 | } 139 | impl Drop for CustomSmartPointer { 140 | fn drop(&mut self) { 141 | println!("Dropping CustomSmartPointer with data `{}`!", self.data); 142 | } 143 | } 144 | pub fn example_drop() { 145 | let mut c = CustomSmartPointer { 146 | data: String::from("my stuff"), 147 | }; 148 | { 149 | let d = CustomSmartPointer { 150 | data: String::from("other stuff"), 151 | }; 152 | c = d; 153 | } 154 | let p1 = &c; 155 | let p2 = &c; 156 | let mp1 = &mut c; 157 | 158 | println!("End of function"); 159 | } 160 | 161 | /* ========== Rc =========== 162 | ========================= */ 163 | pub fn example_rc(){ 164 | // uncomment these 3 lines 165 | // let a = Cons(5, Box::new(Cons(10, Box::new(Nil)))); 166 | // let b = Cons(3, Box::new(a)); 167 | // let c = Cons(4, Box::new(a)); 168 | 169 | let a = Rc::new(RcCons(5, 170 | Rc::new(RcCons(10, 171 | Rc::new(RcNil))))); 172 | println!("count after creating a = {}", Rc::strong_count(&a)); 173 | 174 | let b = RcCons(3, Rc::clone(&a)); 175 | println!("count after creating b = {}", Rc::strong_count(&a)); 176 | { 177 | let c = RcCons(4, Rc::clone(&a)); 178 | println!("count after creating c = {}", Rc::strong_count(&a)); 179 | } 180 | 181 | println!("count after c goes out of scope = {}", Rc::strong_count(&a)); 182 | // QUIZ: what is the sequence of printed numbers? 183 | // 1, 2, 3, 4 | 1, 2, 3, 2 | 1, 2, 3 ,3 184 | 185 | let t = Box::new(10); 186 | let tt = Rc::new(t); 187 | let ttt = (tt.clone()); 188 | let tttt = (tt.clone()); 189 | let y = Rc::new(tt); 190 | // let yy = Rc::new(tt); 191 | } 192 | 193 | use std::rc::Rc; 194 | use self::RcList::{RcCons,RcNil}; 195 | 196 | enum RcList { 197 | RcCons(i32, Rc), 198 | // Cons(i32, Box) 199 | RcNil, 200 | } 201 | 202 | 203 | /* ==== Implicit Deref ===== 204 | ========================= */ 205 | 206 | // Deref coercion is a convenience that Rust performs on arguments 207 | // to functions and methods. 208 | // Deref coercion works only on types that implement the Deref trait. 209 | // 210 | // For example, deref coercion can convert &String to &str because String implements 211 | // the Deref trait such that it returns &str. 212 | // 213 | // 214 | // Deref coercion happens automatically when we pass a reference 215 | // to a particular type’s value as an argument to a function or method 216 | // that doesn’t match the parameter type in the function or method definition. 217 | // A sequence of calls to the deref method converts the type we provided into the type the parameter needs. 218 | // 219 | // Deref coercion was added to Rust so that programmers writing function 220 | // and method calls don’t need to add as many explicit references and dereferences with & and *. 221 | // The deref coercion feature also lets us write more code that can work for either references or smart pointers. 222 | // 223 | // To see an example, we can use our MyBox type. 224 | 225 | // this is an auxiliary function that takes an &str argument 226 | fn hello(name: &str) { 227 | println!("Hello, {name}!"); 228 | } 229 | 230 | pub fn implitictderef() { 231 | let m = MyBox::new(String::from("Rust")); 232 | // Here we’re calling the hello function with the argument &m, 233 | // which is a reference to a MyBox value. 234 | // Because we implemented the Deref trait on MyBox, 235 | // Rust can turn &MyBox into &String by calling deref. 236 | // The standard library provides an implementation 237 | // of Deref on String that returns a string slice, 238 | // and this is in the API documentation for Deref. 239 | // Rust calls deref again to turn the 240 | // &String into &str, which matches the hello function’s definition. 241 | hello(&m); 242 | } 243 | 244 | // If Rust didn't implement deref coercion, we would have to write the following code.// ```rust 245 | // fn main() { 246 | // let m = MyBox::new(String::from("Rust")); 247 | // hello(&(*m)[..]); 248 | // } 249 | 250 | // Similar to how you use the Deref trait to override 251 | // the * operator on immutable references, 252 | // you can use the DerefMut trait to override 253 | // the * operator on mutable references. 254 | 255 | // Rust does deref coercion when it finds types and 256 | // trait implementations in three cases: 257 | // - From `&T` to `&U` when `T: Deref` 258 | // - From `&mut T` to `&mut U` when `T: DerefMut` 259 | // - From `&mut T` to `&U` when `T: Deref` 260 | // 261 | // The first two cases are the same except for mutability. 262 | // The first case states that if you have a &T, and T implements Deref to some type U, 263 | // you can get a &U transparently. 264 | // 265 | // The second case states that the same deref coercion happens for mutable references. 266 | // 267 | // The third case is trickier: 268 | // Rust will also coerce a mutable reference to an immutable one. 269 | // But the reverse is not possible: immutable references 270 | // will never coerce to mutable references. B 271 | // ecause of the borrowing rules, if you have a mutable reference, 272 | // that mutable reference must be the only reference to that data 273 | // (otherwise, the program wouldn’t compile). 274 | 275 | 276 | 277 | /* ========== Arc ========== 278 | ========================= */ 279 | // Similar to Rc, Arc (atomic reference counted) can be used when sharing data across 280 | // threads 281 | // This struct, via the Clone implementation can create a reference pointer 282 | // for the location of a value in the memory heap while increasing the reference counter. 283 | // As it shares ownership between threads, 284 | // when the last reference pointer to a value is out of scope, the variable is dropped. 285 | 286 | 287 | pub fn arc() { 288 | use std::sync::Arc; 289 | use std::thread; 290 | use std::thread::sleep; 291 | 292 | // This variable declaration is where its value is specified. 293 | let apple = Arc::new("the same apple"); 294 | 295 | for _ in 0..10 { 296 | // Here there is no value specification as it is a pointer to a reference in the memory heap. 297 | let apple = Arc::clone(&apple); 298 | // QUIZ: can i replace Arc with Rc? 299 | 300 | let tjh = thread::spawn(move || { 301 | // As Arc was used, threads can be spawned using the value allocated 302 | // in the Arc variable pointer's location. 303 | println!("{:?}", apple); 304 | println!("count after creating apple in a thread: {}", Arc::strong_count(&apple)); 305 | // What's going on? See: 306 | // https://doc.rust-lang.org/std/sync/struct.Arc.html#method.strong_count 307 | }); 308 | // if we wait for the join, then the count always goes to 2 309 | // comment to see the numbers changing 310 | // tjh.join(); 311 | } 312 | 313 | } 314 | 315 | /* 316 | Let's try to understand what is going on with Rc (and Arc) 317 | After all, they're implemented 318 | in Rust 319 | so we should be able to replicate them: 320 | */ 321 | struct NaiveRc { 322 | reference_count: usize, 323 | inner_value: T, 324 | } 325 | 326 | impl Clone for NaiveRc { 327 | fn clone(&self) -> Self { 328 | // QUIZ: why does this code not compile? 329 | // mutability | lifetime | ownership | 330 | // self.reference_count += 1; 331 | 332 | 333 | // 334 | // DNC: error[E0594]: cannot assign to `self.reference_count`, which is behind a `&` reference 335 | // The problem is: clone takes an immutable reference to self, so the reference count can’t be mutated! 336 | return NaiveRc{ 337 | reference_count : self.reference_count, 338 | inner_value : self.inner_value.clone() 339 | } 340 | } 341 | } 342 | impl NaiveRc { 343 | //We could implement a special, differently-named cloning function that takes &mut self, 344 | // but that is awful for usability 345 | // (because it defies the convention of simply checking if a type implements Clone), 346 | // and forces the user of our API to always declare mutable instances of that type 347 | fn clone_mut(&mut self) -> Self{ 348 | self.reference_count += 1; 349 | return NaiveRc{ 350 | reference_count : self.reference_count, 351 | inner_value : self.inner_value.clone(), 352 | } 353 | } 354 | } 355 | // We also know that the reference counted wrappers in the standard library 356 | // (std::rc::Rc and std::sync::Arc) don’t rely on that solution, 357 | // which suggests there’s another way. 358 | // Interior mutability 359 | // which is achieved through the RefCell and Cell types 360 | 361 | 362 | /* ======== RefCell ======== 363 | ========================= */ 364 | //Interior mutability is a design pattern in Rust 365 | // that allows you to mutate data even when there 366 | // are immutable references to that data; 367 | // QUIZ: what rust principles dictate this? 368 | 369 | 370 | 371 | 372 | // 373 | // 374 | // normally, this action is disallowed by the borrowing rules. 375 | // 376 | // To mutate data, the pattern uses **unsafe** code inside a data structure 377 | // to bend Rust’s usual rules that govern mutation and borrowing. 378 | // NOTE: while we're going to use these library types that 379 | // rely on unsafe Rust, we are not ever going to write unsafe 380 | // Rust ourselves. For a good reason. 381 | // 382 | // We will explore this concept using the `RefCell` type, 383 | // which follows the interior mutability pattern. 384 | // Unlike `Rc`, the `RefCell` type represents 385 | // single ownership over the data it holds. 386 | // What makes `RefCell` different from a type like `Box`? 387 | // 388 | // recall the borrowing rules we learned in previous lectures: 389 | // 390 | // - At any given time, you can have either 391 | // one mutable reference or any number of immutable references. 392 | // - References must always be valid. 393 | // 394 | // When using `Box`, the borrowing rules’ invariants 395 | // are enforced at compile time. 396 | // With `RefCell`, these invariants are enforced at 397 | // runtime 398 | // With Box references, if you break these rules, you’ll get a 399 | // compiler error 400 | // With `RefCell`, if you break these rules, your program will 401 | // panic and exit. 402 | // 403 | // Checking borrowing rules at compile time has the advantage of 404 | // catching errors sooner in the development process, 405 | // and there is no impact on runtime performance because all the analysis is completed beforehand. 406 | // For those reasons, checking the borrowing rules at compile time 407 | // is the best choice in the majority of cases. 408 | // 409 | // However, there are other scenarios where one might want 410 | // to take advantage of the additional flexibility afforded by checking 411 | // the borrowing rules at runtime. Because some analyses are impossible, 412 | // if the Rust compiler can’t be sure the code complies with the ownership rules, 413 | // it might reject a correct program; in this way, it is conservative. 414 | // The `RefCell` type is useful when you’re sure your code follows 415 | // the borrowing rules but the compiler is unable to understand and guarantee that. 416 | 417 | //When to use each type? 418 | // - `Rc` enables multiple owners of the same data; ` 419 | // Box` and `RefCell` have single owners. 420 | // - `Box` allows immutable or mutable borrows checked at compile time; 421 | // `Rc` allows only immutable borrows checked at compile time; 422 | // `RefCell` allows immutable or mutable borrows checked at runtime. 423 | // - Because `RefCell` allows mutable borrows checked at runtime, 424 | // you can mutate the value inside the 425 | // `RefCell` even when the `RefCell` is immutable. 426 | 427 | use std::cell::RefCell; 428 | 429 | // the refcell API provides borrow_mut and borrow methods 430 | // to get something of type RefMut or Ref out 431 | // (these implement Deref and Drop) 432 | pub fn refcell_usage() { 433 | let refcell = RefCell::new(10); 434 | println!("refcell {:?}", refcell, ); 435 | let mut mptr = refcell.borrow_mut(); 436 | println!("refcell {:?} + content {}", refcell, *mptr); 437 | *mptr = 11; 438 | println!("refcell {:?} + content {}", refcell, *mptr); 439 | // uncomment for panic. 440 | mem::drop(mptr); 441 | let ptr1 = refcell.borrow(); 442 | let ptr2 = refcell.borrow(); 443 | let ptr3 = refcell.borrow(); 444 | println!("refcell {:?} + content {}",refcell,*ptr1 ); 445 | 446 | // QUIZ: how can i call borrow without panicking? 447 | 448 | 449 | // 450 | // wrap the borrow_mut in a scope to force the drop and this can be uncommented 451 | // or use 452 | // mem::drop(mptr); 453 | } 454 | 455 | // start here 456 | 457 | pub fn refcell_usage_2(){ 458 | let refcell = RefCell::new(10); 459 | println!("refcell {:?}",refcell,); 460 | let mut ptr1 = refcell.borrow(); 461 | let mut ptr2 = refcell.borrow(); 462 | let mut ptr3 = refcell.borrow(); 463 | println!("refcell {:?} + content {}",refcell,*ptr1 ); 464 | println!("refcell {:?} + content {}",refcell,*ptr2 ); 465 | // can we use these read-only pointers though for writing? 466 | // uncomment to find out 467 | // *ptr3 = 11; 468 | // println!("refcell {:?} + content {}",refcell,*ptr3 ); 469 | } 470 | 471 | /* == Interior Mutability == 472 | ========================= */ 473 | // Recall: Interior mutability is a design pattern in Rust that allows you 474 | // to mutate data even when there are immutable references to that data 475 | 476 | //In this example, we’ll create a library that tracks a value against 477 | // a maximum value and sends messages based on how close to the maximum value the current value is. 478 | // This library could be used to keep track of a user’s quota 479 | // for the number of API calls they’re allowed to make, for example. 480 | // Our library will only provide the functionality of tracking 481 | // how close to the maximum a value is and what the messages should be at what times. 482 | // Applications that use our library will be expected to provide 483 | // the mechanism for sending the messages: 484 | // the application could put a message in the application, 485 | // send an email, send a text message, or something else. 486 | // The library doesn’t need to know that detail. 487 | // It only uses a trait we'll provide called Messenger. 488 | // 489 | 490 | // The Messenger trait is the interface our mock object needs to implement 491 | // so that the mock can be used in the same way a real object is. 492 | pub trait Messenger { 493 | fn send(&self, msg: &str); 494 | } 495 | // we need to specify both the lifetime and the type of the messenger field 496 | pub struct LimitTracker<'a, T: Messenger> { 497 | messenger: &'a T, 498 | value: usize, 499 | max: usize, 500 | } 501 | 502 | impl<'a, T> LimitTracker<'a, T> where T: Messenger, { 503 | pub fn new(messenger: &T, max: usize) -> LimitTracker { 504 | LimitTracker { 505 | messenger, 506 | value: 0, 507 | max, 508 | } 509 | } 510 | // The other important part is that we want to test the behavior 511 | // of the set_value method on the LimitTracker. 512 | // We can change what we pass in for the value parameter, 513 | // but set_value doesn’t return anything for us to make assertions on. 514 | // We want to be able to say that if we create a LimitTracker 515 | // with something that implements the Messenger trait and a particular value for max, 516 | // when we pass different numbers for value, 517 | // the messenger is told to send the appropriate messages. 518 | pub fn set_value(&mut self, value: usize) { 519 | self.value = value; 520 | 521 | let percentage_of_max = self.value as f64 / self.max as f64; 522 | 523 | if percentage_of_max >= 1.0 { 524 | self.messenger.send("Error: You are over your quota!"); 525 | } else if percentage_of_max >= 0.9 { 526 | self.messenger 527 | .send("Urgent warning: You've used up over 90% of your quota!"); 528 | } else if percentage_of_max >= 0.75 { 529 | self.messenger 530 | .send("Warning: You've used up over 75% of your quota!"); 531 | } 532 | } 533 | } 534 | 535 | pub mod tests { 536 | // let's use all that is defined outside this module tests 537 | use super::*; 538 | 539 | // We need a mock object that, instead of sending an email or text message when we call send, 540 | // will only keep track of the messages it’s told to send. 541 | struct MockMessenger { 542 | sent_messages: Vec, 543 | } 544 | 545 | impl MockMessenger { 546 | fn new() -> MockMessenger { 547 | MockMessenger { 548 | sent_messages: vec![], 549 | } 550 | } 551 | } 552 | 553 | impl Messenger for MockMessenger { 554 | // adds the message to the mock message vec 555 | fn send(&self, message: &str) { 556 | // QUIZ 557 | // What's the problem with this code? 558 | // Why won't the borrow checker allow this. 559 | // self.sent_messages 560 | // .push(String::from(message)); 561 | 562 | // 563 | // DNC: error[E0596]: cannot borrow `self.sent_messages` as mutable, as it is behind a `&` reference 564 | 565 | // now this may seem counterintuitive: just change the Trait signature! 566 | // alas, sometimes we cannot change trait signatures as we want, 567 | // and sometime we do not want to 568 | // here, we are just testing some functionality, we do not want to change the API 569 | } 570 | } 571 | 572 | // added but not used 573 | pub fn it_sends_an_over_75_percent_warning_message() { 574 | // We can create a new instance of the mock object, 575 | let mock_messenger = MockMessenger::new(); 576 | // create a LimitTracker that uses the mock object, 577 | let mut limit_tracker = LimitTracker::new(&mock_messenger, 100); 578 | // call the set_value method on LimitTracker, 579 | limit_tracker.set_value(80); 580 | // and then check that the mock object has the messages we expect. 581 | // uncomment, and it'll crash 582 | assert_eq!(mock_messenger.sent_messages.len(), 1); 583 | 584 | // This example shows an attempt to do this, but the borrow checker won't allow it. 585 | } 586 | } 587 | // We also can’t take the suggestion from the error text to use &mut self instead, 588 | // because then the signature of send wouldn’t match the signature in the Messenger trait definition. 589 | // 590 | // However, we can fix this example by using interior mutability. 591 | // We will store the sent_messages within a `RefCell`, 592 | // and then the send method will be able to modify hte sent_messages 593 | // to store the messages we've seen. Here is an example implementation. 594 | 595 | pub mod workingtests { 596 | use super::*; 597 | use std::cell::RefCell; 598 | 599 | struct MockMessenger { 600 | // compare to the previous definition! 601 | // The sent_messages field is now of type `RefCell>` 602 | // instead of `Vec`. 603 | sent_messages: RefCell>, 604 | } 605 | 606 | impl MockMessenger { 607 | //In the new function, we create a new `RefCell>` instance around the empty vector. 608 | fn new() -> MockMessenger { 609 | MockMessenger { 610 | sent_messages: RefCell::new(vec![]), 611 | } 612 | } 613 | } 614 | 615 | impl Messenger for MockMessenger { 616 | // compare to the previous def! 617 | // For the implementation of the send method, 618 | // the first parameter is still an immutable borrow of self, 619 | // which matches the trait definition. 620 | // We call borrow_mut on the `RefCell>` in self.sent_messages 621 | // to get a mutable reference to the value inside the `RefCell>`, 622 | // which is the vector. 623 | // Then we can call push on the mutable reference to the vector 624 | // to keep track of the messages sent during the test. 625 | fn send(&self, message: &str) { 626 | self.sent_messages 627 | .borrow_mut() 628 | .push(String::from(message)); 629 | } 630 | } 631 | 632 | pub fn it_sends_an_over_75_percent_warning_message() { 633 | let mock_messenger = MockMessenger::new(); 634 | let mut limit_tracker = LimitTracker::new(&mock_messenger, 100); 635 | limit_tracker.set_value(80); 636 | //The last change we have to make is in the assertion: 637 | // to see how many items are in the inner vector, 638 | // we call borrow on the `RefCell>` to get an immutable reference to the vector. 639 | assert_eq!(mock_messenger.sent_messages.borrow().len(), 1); 640 | } 641 | 642 | // When creating immutable and mutable references, 643 | // we use the & and &mut syntax, respectively. 644 | // With `RefCell`, we use the borrow and borrow_mut methods, 645 | // which are part of the safe API that belongs to `RefCell`. 646 | // The borrow method returns the smart pointer type `Ref`, 647 | // and borrow_mut returns the smart pointer type `RefMut`. 648 | // Both types implement Deref, so we can treat them like regular references. 649 | // 650 | // The `RefCell` keeps track of how many `Ref` and `RefMut` smart pointers 651 | // are currently active. 652 | // Every time we call borrow, the `RefCell` increases its count 653 | // of how many immutable borrows are active. 654 | // When a `Ref` value goes out of scope, 655 | // the count of immutable borrows goes down by one. 656 | // Just like the compile-time borrowing rules, 657 | // `RefCell` lets us have many immutable borrows or one mutable borrow at any point in time. 658 | // 659 | // Now if we violate the borrowing rules, 660 | // we'll get an error at compile time rather than at runtime. Here is an example. 661 | 662 | // QUIZ: what trait implementations are needed to express this behaviour? 663 | 664 | // uncomment this and comment the other impl Messenger for MockMessenger above 665 | // impl Messenger for MockMessenger { 666 | // fn send(&self, message: &str) { 667 | // let mut one_borrow = self.sent_messages.borrow_mut(); 668 | // let mut two_borrow = self.sent_messages.borrow_mut(); 669 | // 670 | // one_borrow.push(String::from(message)); 671 | // two_borrow.push(String::from(message)); 672 | // } 673 | // } 674 | 675 | // We can see that with refcell this example will compile, but when we run it the program will panic. 676 | // 677 | // Notice that the code panicked with the message already borrowed: 678 | // BorrowMutError. This is how `RefCell` handles violations of the borrowing rules at runtime. 679 | } 680 | 681 | /* ====== Rc + RefCell ===== 682 | ========================= */ 683 | // It is common to use `RefCell` together with `Rc`. 684 | // Recall that `Rc` lets you have multiple owners of some data, 685 | // but it only gives immutable access to that data. 686 | // If you have an `Rc` that holds a `RefCell`, 687 | // you can get a value that can have multiple owners and that you can mutate! 688 | // Because `Rc` holds only immutable values, 689 | // we can’t change any of the values in the list once we’ve created them. 690 | // Now we will add in `RefCell` to gain the ability to change the values. 691 | 692 | pub mod rc_plus_refcell { 693 | #[derive(Debug)] 694 | enum List { 695 | Cons(Rc>, Rc), 696 | Nil, 697 | } 698 | 699 | use std::cell::RefCell; 700 | use std::rc::Rc; 701 | use self::List::{Cons,Nil}; 702 | 703 | pub fn examplepcrefcell() { 704 | // Here we create a value that is an instance of `Rc>` 705 | // and store it in a variable named value so we can access it directly later. 706 | let value = Rc::new(RefCell::new(5)); 707 | //Then we create a List in a with a Cons variant that holds value. 708 | // We need to clone value so both a and value have ownership of the inner 5 value 709 | // rather than transferring ownership from value to a or having a borrow from value. 710 | let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil))); 711 | // We wrap the list a in an `Rc` so when we create lists b and c, 712 | // they can both refer to a. 713 | let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a)); 714 | let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a)); 715 | // Graph: 716 | /* 717 | { Nil } <----------------| 718 | | 719 | { 5 } <------------| | 720 | | | 721 | |---------------> |-> a ---> [ | , | ] 722 | | | 723 | | b ----> [ | , |] 724 | | |----------------------------> { 3 } 725 | | 726 | |-----------------| 727 | |---+----------> { 4 } 728 | c ----> [ | , |] 729 | 730 | */ 731 | // After we’ve created the lists in a, b, and c, we add 10 to the value in value. 732 | // We do this by calling borrow_mut on value, 733 | // QUIZ: what Rust feature is used here under the hood? 734 | let x = *value.borrow_mut() += 10; 735 | // 736 | // 737 | 738 | // 739 | // which uses implicit dereferencing 740 | // to dereference the `Rc` to the inner `RefCell` value. 741 | // The borrow_mut method returns a `RefMut` smart pointer, 742 | // and we use the dereference operator on it to change the inner value. 743 | // When we print a, b, and c, we can see that they all have the modified value of 15 rather than 5. 744 | println!("a after = {:?}", a); 745 | println!("b after = {:?}", b); 746 | println!("c after = {:?}", c); 747 | 748 | } 749 | // Here we have an outwardly immutable List value. 750 | // But we can use the methods on `RefCell` that provide access 751 | // to its interior mutability so we can modify our data when we need to. 752 | // The runtime checks of the borrowing rules protect us from data races, 753 | // and it’s sometimes worth trading a bit of speed for this flexibility in our data structures. 754 | } 755 | 756 | // Smart pointers are Data Pointers 757 | // think about the C pointer and RC> 758 | // and their capabilities 759 | // certain language features are there to limit the usage of pointers in Rust 760 | // and do not limit this in C 761 | 762 | 763 | 764 | /* === Reference cycles ==== 765 | ========================= */ 766 | //Rust’s memory safety guarantees make it difficult, but not impossible, 767 | // to accidentally create memory that is never cleaned up (known as a memory leak). 768 | // Notice that memory leakage is not a memory safety vulnerability! 769 | // 770 | // Preventing memory leaks entirely is not one of Rust’s guarantees 771 | // in the same way that disallowing data races at compile time is. 772 | // We can see that Rust allows memory leaks by using `Rc` and `RefCell`: 773 | // it’s possible to create references where items refer to each other in a cycle. 774 | // This creates memory leaks because the reference count of each item 775 | // in the cycle will never reach 0, and the values will never be dropped. 776 | pub mod overflow { 777 | use std::cell::RefCell; 778 | use std::rc::Rc; 779 | use self::List::{Cons, Nil}; 780 | 781 | #[derive(Debug)] 782 | enum List { 783 | Cons(i32, RefCell>), 784 | Nil, 785 | } 786 | 787 | impl List { 788 | fn tail(&self) -> Option<&RefCell>> { 789 | match self { 790 | Cons(_, item) => Some(item), 791 | Nil => None, 792 | } 793 | } 794 | fn head(&self) -> Option<&i32> { 795 | match self { 796 | Nil => None, 797 | Cons( e, _) => Some(e) 798 | } 799 | } 800 | } 801 | 802 | pub fn exampleoverflow() { 803 | let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil)))); 804 | 805 | println!("a initial rc count = {}", Rc::strong_count(&a)); 806 | println!("a next item = {:?}", a.tail()); 807 | let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a)))); 808 | println!("a rc count after b creation = {}", Rc::strong_count(&a)); 809 | println!("b initial rc count = {}", Rc::strong_count(&b)); 810 | println!("b next item = {:?}", b.tail()); 811 | 812 | if let Some(link) = a.tail() { 813 | *link.borrow_mut() = Rc::clone(&b); 814 | } 815 | println!("b rc count after changing a = {}", Rc::strong_count(&b)); 816 | println!("a rc count after changing a = {}", Rc::strong_count(&a)); 817 | // Uncomment the next line to see that we have a cycle; 818 | // QUIZ: What will happen? 819 | println!("a next item = {:?}", a.head()); 820 | 821 | // 822 | // and here? 823 | println!("a next item = {:?}", a.tail()); 824 | } 825 | } 826 | // We will overflow the stack. 827 | // The reference count of the `Rc` instances in both a and b are 2 828 | // after we change the list in a to point to b. 829 | // At the end of main, Rust drops the variable b, 830 | // which decreases the reference count of the `Rc` instance from 2 to 1. 831 | // The memory that `Rc` has on the heap won’t be dropped at this point, 832 | // because its reference count is 1, not 0. 833 | // Then Rust drops a, which decreases the reference count of the a `Rc` instance from 2 to 1 as well. 834 | // This can’t be dropped either, because the other `Rc` instance still refers to it. 835 | 836 | 837 | /* ======== Graphs ========= 838 | ========================= */ 839 | // For example, when connecting nodes in a graph. 840 | // You may think to wrap the nodes in Rc or Arc and call it a day. 841 | // That a perfectly reasonable line of though, and it would work 842 | // if you never, ever needed to mutate nodes. 843 | // Once you try building the graph by incrementally adding and connecting nodes, 844 | // the compiler will give you grief. 845 | // You could call get_mut to receive an Option<&mut T>, 846 | // but that would work only once: get_mut only returns a mutable reference 847 | // as if there is only one “strong” reference to the value... Foiled again! 848 | // 849 | // Fortunately, you can use interior mutability here: 850 | // use Rc> or Rc>. 851 | // That way you can clone the reference-counted wrapper as much as you want 852 | // and still modify the innermost value wrapped by Cell or RefCell. 853 | 854 | 855 | // A graph can be represented in several ways. For the sake of illustrating how 856 | // interior mutability works in practice, let's go with the simplest 857 | // representation: a list of nodes. 858 | struct Graph { 859 | nodes: Vec>, 860 | } 861 | // Each node has an inner value and a list of adjacent nodes it is connected to 862 | // (through a directed edge). 863 | struct Node(NodeRef); 864 | // The private representation of a node. 865 | struct _Node { 866 | inner_value: T, 867 | adjacent: Vec>, 868 | } 869 | // That list of adjacent nodes cannot be the exclusive owner of those nodes, or 870 | // else each node would have at most one edge to another node and the graph 871 | // couldn't also own these nodes. 872 | // We need to wrap Node with a reference-counted box, such as Rc or Arc. We'll 873 | // go with Rc, because this is a toy example. 874 | type NodeRef = Rc>>; 875 | // However, Rc and Arc enforce memory safety by only giving out shared 876 | // (i.e., immutable) references to the wrapped object, and we need mutability to 877 | // be able to connect nodes together. 878 | // The solution for this problem is wrapping Node in either Cell or RefCell, to 879 | // restore mutability. We're going to use RefCell because Node doesn't 880 | // implement Copy (we don't want to have independent copies of nodes!). 881 | 882 | impl Node { 883 | // Creates a new node with no edges. 884 | fn new(inner: T) -> Node { 885 | let node = _Node { 886 | inner_value: inner, 887 | adjacent: vec![] 888 | }; 889 | Node(Rc::new(RefCell::new(node))) 890 | } 891 | 892 | // Adds a directed edge from this node to other node. 893 | fn add_adjacent(&self, other: &Node) { 894 | (self.0.borrow_mut()).adjacent.push(other.0.clone()); 895 | } 896 | } 897 | 898 | impl Graph { 899 | fn with_nodes(nodes: Vec>) -> Self { 900 | Graph { nodes: nodes } 901 | } 902 | } 903 | 904 | pub fn graphexample() { 905 | // Create some nodes 906 | let node_1 = Node::new(1); 907 | let node_2 = Node::new(2); 908 | let node_3 = Node::new(3); 909 | 910 | // Connect some of the nodes (with directed edges) 911 | node_1.add_adjacent(&node_2); 912 | node_1.add_adjacent(&node_3); 913 | node_2.add_adjacent(&node_1); 914 | node_3.add_adjacent(&node_1); 915 | 916 | // Add nodes to graph 917 | let graph = Graph::with_nodes(vec![node_1, node_2, node_3]); 918 | 919 | // Show every node in the graph and list their neighbors 920 | for node in graph.nodes.iter().map(|n| n.0.borrow()) { 921 | let value = node.inner_value; 922 | let neighbours = node.adjacent.iter() 923 | .map(|n| n.borrow().inner_value) 924 | .collect::>(); 925 | println!("node ({}) is connected to: {:?}", value, neighbours); 926 | } 927 | } 928 | // If you ignore the loop that prints out the graph’s information, 929 | // now the user doesn’t know how a Node is implemented. 930 | // This version’s usability can still be improved by implementing 931 | // the std::fmt::Debug trait for Node and Graph, for instance. 932 | 933 | // You can play with this example in the Rust Playground: 934 | // https://play.rust-lang.org/?gist=9ccf40fae2347519fcae7dd42ddf5ed6 935 | // Try changing some things yourself! I find breaking things helps me consolidate new knowledge: 936 | // Replacing RefCell with Cell 937 | // Removing RefCell and using Rc> 938 | // Removing Rc and using RefCell> 939 | 940 | /* ========= Cell ========== 941 | ========================= */ 942 | // Finally, let's mention Cell too, which can be also used in place of RefCell for interior mutability 943 | // The most obvious difference between Cell and RefCell is that 944 | // RefCell makes run-time borrow checks, while Cell does not. 945 | // Cell is quite simple to use: 946 | // you can read and write a Cell’s inner value by calling get or set on it. 947 | // Since there are no compile-time or run-time checks, 948 | // you do have to be careful to avoid some bugs the borrow checker would stop you from writing, 949 | // such as accidentally overwriting the wrapped value: 950 | use std::cell::Cell; 951 | use std::time::Duration; 952 | 953 | // like RefCell, Cell implements interior mutability: 954 | // mutation of values in an immutable context. 955 | fn foo(cell: &Cell) { 956 | let value = cell.get(); 957 | cell.set(value * 2); 958 | } 959 | pub fn cellexamplee() { 960 | let cell = Cell::new(1); 961 | let new_value = cell.get() + 10; 962 | println!("cell value : {}", cell.get()); 963 | // foo is going to do stuff with the cell 964 | foo(&cell); 965 | println!("cell value : {}", cell.get()); 966 | cell.set(new_value); 967 | // but we can still change its contents afterwards 968 | // QUIZ: what will this print? 969 | println!("cell value : {}", cell.get()); 970 | 971 | // 972 | // notice the `cell` is not mutable! 973 | } 974 | // so the Cell is always mutable 975 | // In contrast, a RefCell requires you to call borrow or borrow_mut 976 | // (immutable and mutable borrows) before using it, yielding a pointer to the value. 977 | // Its borrow semantics are identical to externally mutable variables: 978 | // you can have either a mutable borrow on the inner value or several immutable borrows, 979 | // so the kind of bug I mentioned earlier is detected in run-time. 980 | 981 | // A significant difference between Cell and RefCell is that 982 | // Cell requires that the inner value T implements Copy, 983 | // while RefCell has no such restriction. 984 | // Often, you won’t want copy semantics on your wrapped types, so you’ll have to use RefCell. 985 | // 986 | // Put succinctly, 987 | // Cell has Copy semantics and provides values 988 | // RefCell has move semantics and provides references. 989 | 990 | // we define our Rc with Cell 991 | struct NaiveRcWithCell { 992 | inner_value: T, 993 | references: Cell, 994 | } 995 | 996 | // implement its creation 997 | impl NaiveRcWithCell { 998 | fn new(inner: T) -> Self { 999 | NaiveRcWithCell { 1000 | inner_value: inner, 1001 | references: Cell::new(1), 1002 | } 1003 | } 1004 | fn references(&self) -> usize { 1005 | self.references.get() 1006 | } 1007 | } 1008 | // and implement its cloning, in a way that allows us to increment the references 1009 | impl Clone for NaiveRcWithCell { 1010 | fn clone(&self) -> Self { 1011 | self.references.set(self.references.get() + 1); 1012 | NaiveRcWithCell { 1013 | inner_value: self.inner_value.clone(), 1014 | references: self.references.clone(), 1015 | } 1016 | } 1017 | } 1018 | // now we use our Cell Rc, and the counts are all correct! 1019 | pub fn rcwithcellexample() { 1020 | let wrapped = NaiveRcWithCell::new("Hello!"); 1021 | println!("references before cloning: {:?}", wrapped.references()); 1022 | let wrapped_clone = wrapped.clone(); 1023 | println!("references after cloning: {:?}", wrapped.references()); 1024 | println!("clone references: {:?}", wrapped_clone.references()); 1025 | let wrapped_clone2 = wrapped.clone(); 1026 | println!("references after cloning2: {:?}", wrapped.references()); 1027 | println!("references after cloning2: {:?}", wrapped_clone.references()); 1028 | println!("references after cloning2: {:?}", wrapped_clone2.references()); 1029 | } 1030 | 1031 | 1032 | // Calling borrow or borrow_mut on a mutably borrowed RefCell will cause a panic, 1033 | // as will calling borrow_mut on a immutably borrowed value. 1034 | // This aspect makes RefCell unsuitable to be used in a parallel scenario; 1035 | // you should use a thread-safe type (like a Mutex or a RwLock, for example) instead. 1036 | // 1037 | 1038 | pub mod par{ 1039 | use std::cell::RefCell; 1040 | use std::sync::{Arc, Mutex}; 1041 | use std::thread; 1042 | 1043 | pub fn arcmutex() { 1044 | // let counter = Arc::new(RefCell::new(0)); 1045 | // [DNC] error[E0277]: `RefCell` cannot be shared between threads safely 1046 | let counter = Arc::new(Mutex::new(0)); 1047 | let mut handles = vec![]; 1048 | 1049 | for _ in 0..10 { 1050 | let counter = Arc::clone(&counter); 1051 | let handle = thread::spawn(move || { 1052 | let mut num = counter.lock().unwrap(); 1053 | println!("I am thread number {}",num); 1054 | *num += 1; 1055 | }); 1056 | handles.push(handle); 1057 | } 1058 | for handle in handles { 1059 | handle.join().unwrap(); 1060 | } 1061 | println!("Result: {}", *counter.lock().unwrap()); 1062 | } 1063 | } 1064 | 1065 | // A RefCell will stay “locked” until the pointer you received falls out of scope, 1066 | // so you might want to declare a new block scope (ie., { ... }) while working with the borrowed value, 1067 | // or even explicitly drop the borrowed value when you’re done with it, to avoid unpleasant surprises. 1068 | -------------------------------------------------------------------------------- /src/classes/c12_fp.rs: -------------------------------------------------------------------------------- 1 | /// This module shows some KEY concepts of Rust related to 2 | /// functional programming 3 | /// closure 4 | /// iterators 5 | /// See 6 | /// https://doc.rust-lang.org/book/ch13-00-functional-features.html 7 | /// 8 | //Rust’s design has taken inspiration from many existing languages and techniques, 9 | // and one significant influence is functional programming. 10 | // Programming in a functional style often includes 11 | // using functions as values by passing them in arguments, 12 | // returning them from other functions, 13 | // assigning them to variables for later execution, 14 | // and so forth. 15 | // 16 | // In this section we'll discuss some features of Rust 17 | // that are similar to features in many languages often referred to as functional. 18 | // Two important features include 19 | // closures 20 | // iterators 21 | 22 | /* ======= Closures ======== 23 | ========================= */ 24 | pub mod closures{ 25 | 26 | // Rust’s closures are anonymous functions you can save in a variable 27 | // or pass as arguments to other functions. 28 | // You can create the closure in one place 29 | // and then call the closure elsewhere to evaluate it in a different context. 30 | // Unlike functions, closures can capture values from the scope in which they’re defined. 31 | // We’ll demonstrate how these closure features allow for code reuse and behavior customization. 32 | 33 | // A function that increments by 1 34 | fn function(i: i32) -> i32 { i + 1 } 35 | 36 | pub fn closuresexample() { 37 | // Closures are anonymous, here we are binding them to references 38 | // Annotation is identical to function annotation but is optional 39 | // as are the `{}` wrapping the body. These nameless functions 40 | // are assigned to appropriately named variables. 41 | let closure_annotated = |i: i32| -> i32 { i + 1 }; 42 | // between | and | is the formal parameter 43 | let closure_inferred = |i| i + 1; 44 | // type annotations are optional, as are curly braces 45 | // don't be fools and use them 46 | 47 | let i = 1; 48 | // Call the function and closures. 49 | println!("function: {}", function(i)); 50 | println!("closure_annotated: {}", closure_annotated(i)); 51 | println!("closure_inferred: {:?}", closure_inferred(i)); 52 | 53 | // A closure taking no arguments which returns an `i32`. 54 | let one = || -> i32 { 1 }; 55 | println!("closure returning one: {}", one()); 56 | 57 | // Closures are usually short and relevant only within a narrow context 58 | // rather than in any arbitrary scenario. 59 | // Within these limited contexts, the compiler is reliably able to infer the types 60 | // of the parameters and the return type, similar to how it’s able to infer the types of most variables. 61 | 62 | // Closures (and functions) can be used as inputs and as output to other functions 63 | // QUIZ: this makes the language: 64 | 65 | 66 | 67 | 68 | // higher-order 69 | let closure = |x : i32| -> i32 { println!("I'm a closure with {}! ", x);x }; 70 | 71 | call_me(closure); 72 | call_me(function); 73 | } 74 | 75 | fn call_me i32>(f: F) { 76 | f(1); 77 | } 78 | 79 | // Closures as input parameters are possible, 80 | // so returning closures as output parameters should also be possible. 81 | // Before we deal with them, we need to explain capturing, and the related traits 82 | 83 | // capturing is not a necessary feature of closures 84 | // you can write closures that do not capture anything, and that's still very FP 85 | // but when capturing, things get real messy, real quick 86 | 87 | //Closures are inherently flexible and will do what the functionality requires 88 | // to make the closure work without annotation. 89 | // This allows capturing to flexibly adapt to the use case, 90 | // sometimes moving and sometimes borrowing. 91 | // Closures can capture variables: 92 | // by reference: &T 93 | // by mutable reference: &mut T 94 | // by value: T 95 | // They preferentially capture variables by reference 96 | // and only go lower when required. 97 | 98 | pub fn capturingexample(){ 99 | use std::mem; 100 | // A closure to print `color` which immediately borrows (`&`) `color` and 101 | // stores the borrow and closure in the `print` variable. It will remain 102 | // borrowed until `print` is used the last time. 103 | // 104 | // `println!` only requires arguments by immutable reference so it doesn't 105 | // impose anything more restrictive. 106 | // LOOK CLOSELY! the body of the print closure is referring to color! 107 | 108 | let color = String::from("green"); 109 | let print2 = |color:String| {println!("{}",color);}; 110 | // < fn print2, [] > 111 | let print23 = || { 112 | let color = "wghat".to_string(); 113 | println!("{}",color); 114 | // return || {return 0;} 115 | }; 116 | // < fn print3, [] > 117 | 118 | let print = || { println!("`color`: {}", color) }; 119 | // < fn print, [ color -> "green" ] > 120 | // Call the closure using the borrow. 121 | print(); 122 | 123 | // QUIZ: can we do this: 124 | // Y | N 125 | let _reborrow = &color; 126 | print(); 127 | 128 | // 129 | // `color` can be borrowed immutably again, because the closure only holds 130 | // an immutable reference to `color`. 131 | 132 | // A move or reborrow is allowed after the final use of `print` 133 | let _color_moved = color; 134 | 135 | let mut count = 0; 136 | // A closure to increment `count` could take either `&mut count` or `count` 137 | // but `&mut count` is less restrictive so it takes that. Immediately 138 | // borrows `count`. 139 | // 140 | // A `mut` is required on `inc` because a `&mut` is stored inside. Thus, 141 | // calling the closure mutates the closure which requires a `mut`. 142 | let mut inc = || { 143 | count += 1; 144 | println!("`count`: {}", count); 145 | }; 146 | // let mut inc2 = || { 147 | // count +=1; 148 | // }; 149 | 150 | // Call the closure using a mutable borrow. 151 | inc(); 152 | 153 | // The closure still mutably borrows `count` because it is called later. 154 | // QUIZ: can we do this, given the inc() below? 155 | // let _reborrow = &count; 156 | inc(); 157 | 158 | // The closure no longer needs to borrow `&mut count`, 159 | // because it is no longer used in the code below. 160 | // Therefore, it is possible to reborrow without an error 161 | let _count_reborrowed = &mut count; 162 | 163 | // A non-copy type. 164 | let mut movable = Box::new(3); 165 | 166 | // `mem::drop` requires `T` so this must take by value. A copy type 167 | // would copy into the closure leaving the original untouched. 168 | // A non-copy must move and so `movable` immediately moves into 169 | // the closure. 170 | let consume = || { 171 | println!("`movable`: {:?}", &movable); 172 | *movable = 5; 173 | mem::drop(movable); 174 | }; 175 | 176 | // let x = *movable; 177 | // `consume` consumes the variable so this can only be called once. 178 | consume(); 179 | // DNC: error[E0382]: use of moved value: `consume` 180 | // consume(); 181 | // The error is interesting: 182 | // 153 | consume(); 183 | // | ^^^^^^^ 184 | // note: this value implements `FnOnce`, which causes it to be moved when called 185 | } 186 | 187 | // While Rust chooses how to capture variables on the fly mostly without type annotation, 188 | // this ambiguity is not allowed when writing functions. 189 | // When taking a closure as an input parameter, 190 | // the closure's complete type must be annotated using one of a few traits, 191 | // and they're determined by what the closure does with captured value. 192 | // In order of decreasing restriction, they are: 193 | // Fn: the closure uses the captured value by reference (&T) 194 | // FnMut: the closure uses the captured value by mutable reference (&mut T) 195 | // FnOnce: the closure uses the captured value by value (T) 196 | // On a variable-by-variable basis, the compiler will capture variables in the least restrictive manner possible. 197 | // 198 | // For instance, consider a parameter annotated as FnOnce. 199 | // This specifies that the closure may capture by &T, &mut T, or T, 200 | // but the compiler will ultimately choose 201 | // based on how the captured variables are used in the closure. 202 | // 203 | // This is because if a move is possible, 204 | // then any type of borrow should also be possible. 205 | // Note that the reverse is not true. 206 | // If the closure is annotated as Fn, then capturing variables by &mut T or T are not allowed. 207 | 208 | // let's look at an example 209 | 210 | // A function which takes a closure as an argument and calls it. 211 | // denotes that F is a "Generic type parameter" 212 | fn apply_FnOnce(f: F) where F: FnOnce() { 213 | // Note: The closure takes no input and returns nothing. 214 | f(); 215 | } 216 | fn apply_Fn(f: F) where F: Fn() { 217 | // Note: The type of F has changed 218 | f(); 219 | } 220 | fn apply_FnMut(mut f: F) where F: FnMut() { 221 | // Note: The type of F has changed and we needed to add the `mut` to F 222 | f(); 223 | } 224 | 225 | // A function which takes a closure and returns an `i32`. 226 | fn apply_to_3(f: F) -> i32 where F: Fn(i32) -> i32 { 227 | // Note: The closure takes an `i32` and returns an `i32`. 228 | f(3) 229 | } 230 | // the trait bound of F specifies the 231 | fn applytest(f:F) -> i32 where F:FnOnce(i32) -> i32 { 232 | 3 233 | } 234 | 235 | pub fn fntypes(){ 236 | use std::mem; 237 | 238 | let greeting = "hello"; 239 | // A non-copy type. 240 | // `to_owned` creates owned data from borrowed one 241 | let mut farewell = "goodbye".to_owned(); 242 | 243 | // Capture 2 variables: `greeting` by reference and 244 | // `farewell` by value. 245 | let mut diary = || { 246 | // `greeting` is by reference: requires `Fn`. 247 | println!("I said {}.", greeting); 248 | // Mutation forces `farewell` to be captured by 249 | // mutable reference. Now requires `FnMut`. 250 | farewell.push_str("!!!"); 251 | println!("Then I screamed {}.", farewell); 252 | println!("Now I can sleep. zzzzz"); 253 | // Manually calling drop forces `farewell` to 254 | // be captured by value. Now requires `FnOnce`. 255 | mem::drop(farewell); 256 | }; 257 | 258 | // Call the function which applies the closure. 259 | // QUIZ: which one compiles? 260 | apply_FnOnce(diary); 261 | // apply_FnMut(&mut diary); 262 | // apply_Fn(&mut diary); 263 | 264 | // 265 | // 266 | let _reb = &greeting; 267 | 268 | // uncomment below: 269 | // DNC: error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce` 270 | // QUIZ: what do i need to comment in the code of `diary` to make this work 271 | // apply_FnMut(diary); 272 | 273 | // 274 | // if we comment the `mem::drop``, and the previous usage, this works 275 | // uncomment below 276 | // DNC: error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` 277 | // 278 | 279 | // 280 | // we need to comment both the `mem::drop` and the `farewell.push` to make this go 281 | // and now we can call both apply_FnOnce and apply_FnMut too 282 | 283 | // `double` satisfies `apply_to_3`'s trait bound 284 | let double = |x| 2 * x; 285 | 286 | println!("3 doubled: {}", apply_to_3(double)); 287 | } 288 | 289 | // here 290 | 291 | // Fn : usable + : uses its env to R 292 | // FnMut : usable + : uses its env to R/W 293 | // FnOnce : usable 1 : uses its env to R/W/D 294 | // Trait heirarchy: Fn <: FnMut <: FnOnce 295 | // -> what can we do with each of them? 296 | // the point is always: the more i go down the hierarchy, the more i can do 297 | // the top can only be used once, the types below can be used multiple times 298 | // How to use these? 299 | // Use FnOnce as a bound when you want to accept a parameter of function-like 300 | // type and only need to call it once. If you need to call the parameter repeatedly, 301 | // use FnMut as a bound; if you also need it to not mutate state, use Fn. 302 | // 303 | 304 | // back to the code above: 305 | // - with diary being FN, we can pass it to any function 306 | // - with diary being FnMut : we need it to be mut, we can only pass it as a &mut to apply_fnmut and apply_fnonce 307 | // careful not to move diary, but to pass it as ref! 308 | // - with diary being FnOnce we can pass it only to apply_fnonce 309 | 310 | 311 | // Back to Returning closures 312 | // Anonymous closure types are, by definition, 313 | // unknown, so we have to use impl Trait to return them. 314 | // 315 | // The valid traits for returning a closure are 316 | // Fn 317 | // FnMut 318 | // FnOnce 319 | // Beyond this, the 320 | // move 321 | // keyword must be used, 322 | // which signals that all captures occur by value. 323 | // Why do we need this? 324 | 325 | // 326 | // This is required because any captures by reference would be dropped 327 | // as soon as the function exited, leaving invalid references in the closure 328 | 329 | // we define 3 functions that return closures 330 | // see the impl in the return type 331 | fn create_fn() -> impl Fn() { 332 | let text = "Fn".to_owned(); 333 | return move || { 334 | println!("This is a: {}", text); 335 | }; 336 | } 337 | 338 | fn create_fnmut() -> impl FnMut() { 339 | let text = "FnMut".to_owned(); 340 | move || println!("This is a: {}", text) 341 | } 342 | 343 | fn create_fnonce() -> impl FnOnce() { 344 | let text = "FnOnce".to_owned(); 345 | move || println!("This is a: {}", text) 346 | } 347 | 348 | // fn create_fn_para<'a>(text : &'a String) -> impl Fn() + 'a { 349 | fn create_fn_para<'a,'b : 'a>(text : &'a String, t2 : &'b String) -> Box { 350 | return Box::new( move || { 351 | println!("This is a: {}, {}", & text, & t2); 352 | }); 353 | } 354 | 355 | pub fn closures_output(){ 356 | let fn_plain = create_fn(); 357 | let mut fn_mut = create_fnmut(); 358 | let fn_once = create_fnonce(); 359 | 360 | fn_plain(); 361 | fn_plain(); 362 | fn_mut(); 363 | fn_once(); 364 | // fn_once(); 365 | } 366 | 367 | // now a big FP example that uses closures 368 | 369 | fn is_odd(n: u32) -> bool { 370 | n % 2 == 1 371 | } 372 | 373 | // closures are used a lot in Options and Iterators 374 | pub fn fprules() { 375 | println!("Find the sum of all the squared odd numbers under 1000"); 376 | let upper = 1000; 377 | 378 | // Imperative approach 379 | // Declare accumulator variable 380 | let mut acc = 0; 381 | // Iterate: 0, 1, 2, ... to infinity, but there's a break in the loop below for a threshold 382 | for n in 0.. { 383 | // Square the number 384 | let n_squared = n * n; 385 | 386 | if n_squared >= upper { 387 | // Break loop if exceeded the upper limit 388 | break; 389 | } else if is_odd(n_squared) { 390 | // Accumulate value, if it's odd 391 | acc += n_squared; 392 | } 393 | } 394 | println!("imperative style: {}", acc); 395 | 396 | // Functional approach 397 | let sum_of_squared_odd_numbers: u32 = 398 | // again, from 0 to infinity 399 | (0..). 400 | // All natural numbers squared 401 | map(|n| n * n) 402 | // Below upper limit: stop when reaching over upper 403 | .take_while(|&n_squared| n_squared < upper) 404 | // keep only those that are odd 405 | .filter(|&n_squared| is_odd(n_squared)) 406 | // Sum them 407 | .fold(0, |acc, n_squared| acc + n_squared); 408 | println!("functional style: {}", sum_of_squared_odd_numbers); 409 | } 410 | 411 | } 412 | 413 | /* ======= Iterators ======= 414 | ========================= */ 415 | 416 | pub mod iterators{ 417 | // The iterator pattern allows you to perform some task on a sequence of items in turn. 418 | // An iterator is responsible for the logic of iterating over each item 419 | // and determining when the sequence has finished. 420 | // When you use iterators, you don’t have to reimplement that logic yourself. 421 | 422 | // In Rust, iterators are lazy, 423 | // meaning they have no effect until you call methods that consume the iterator to use it up. 424 | pub fn iteratorexample(){ 425 | // For example, the code in the example below creates an iterator 426 | // over the items in the vector v1 by calling 427 | // the iter method defined on Vec\. 428 | // This code by itself doesn’t do anything useful. 429 | let mut v1 = vec![1, 2, 3]; 430 | let v1_iter = v1.iter_mut(); 431 | 432 | // These iterators can be used in a variety of ways. 433 | // For example, we can separate the creation of the iterator 434 | // from the use of the iterator in the for loop, as shown below. 435 | for val in v1_iter { 436 | println!("Got: {}", val); 437 | } 438 | //Other languages that don’t have iterators provided by their standard libraries, 439 | // you would likely write this same functionality by starting a variable at index 0, 440 | // using that variable to index into the vector to get a value, 441 | // and incrementing the variable value in a loop until it reached the total number of items in the vector. 442 | // 443 | // Iterators handle all that logic for you, 444 | // cutting down on repetitive code you could potentially mess up. 445 | // Iterators give you more flexibility to use the same logic 446 | // with many different kinds of sequences, 447 | // not just data structures you can index into, like vectors. 448 | // 449 | // Let’s examine how iterators do that. 450 | 451 | // All iterators implement a trait named Iterator 452 | // that is defined in the standard library. 453 | // The definition of the trait looks like this: 454 | 455 | // pub trait Iterator { 456 | // type Item; 457 | // 458 | // fn next(&mut self) -> Option; 459 | // // methods with default implementations elided 460 | // } 461 | 462 | // Notice this definition uses some new syntax: 463 | // type Item and Self::Item, 464 | // which are defining an associated type with this trait. 465 | // For now, all you need to know is that this code says 466 | // implementing the Iterator trait 467 | // requires that you also define an Item type, 468 | // and this Item type is used in the return type of the next method. 469 | // In other words, the Item type will be the type returned from the iterator. 470 | // 471 | // The Iterator trait only requires implementors to define one method: 472 | // the next method, which returns one item of the iterator at a time 473 | // wrapped in Some and, when iteration is over, returns None. 474 | 475 | let v1 = vec![1, 2, 3]; 476 | 477 | // Note that we need to make v1_iter mutable: 478 | // calling the next method on an iterator changes internal state 479 | // that the iterator uses to keep track of where it is in the sequence. 480 | // In other words, this code consumes, or uses up, the iterator. 481 | // Each call to next eats up an item from the iterator. 482 | // We didn’t need to make v1_iter mutable when we used a for loop 483 | // because the loop took ownership of v1_iter and made it mutable behind the scenes. 484 | let mut v1_iter = v1.iter(); 485 | // QUIZ: what does v1_iter.next() return? 486 | 487 | // 488 | // 489 | assert_eq!(v1_iter.next(), Some(&1)); 490 | assert_eq!(v1_iter.next(), Some(&2)); 491 | assert_eq!(v1_iter.next(), Some(&3)); 492 | assert_eq!(v1_iter.next(), None); 493 | 494 | // start here 495 | 496 | //WG - coordination / arrangement / proactiveness 497 | // learning goals of the class wrt WG 498 | 499 | // Also note that the values we get from the calls to next are immutable references 500 | // to the values in the vector. 501 | // The iter method produces an iterator over immutable references. 502 | // If we want to create an iterator that takes ownership of `v1` 503 | // and returns owned values, we can call into_iter instead of iter. 504 | // Similarly, if we want to iterate over mutable references, 505 | // we can call iter_mut instead of iter. 506 | 507 | // Methods can be written to consume the iterator, 508 | // and these that call next are called consuming adaptors, 509 | // because calling them uses up the iterator. 510 | 511 | let v1 = vec![1, 2, 3]; 512 | let v1_iter = v1.iter(); 513 | // One example is the sum method, which takes ownership of the iterator 514 | // and iterates through the items by repeatedly calling next, 515 | // thus consuming the iterator. 516 | // As it iterates through, it adds each item to a running total 517 | // and returns the total when iteration is complete. 518 | let total: i32 = v1_iter.sum(); 519 | 520 | // DNC: error[E0596]: cannot borrow `v1_iter` as mutable, as it is not declared as mutable 521 | // We aren’t allowed to use v1_iter after the call to sum because sum takes ownership of the iterator we call it on. 522 | // let v2 = v1_iter.next(); 523 | 524 | // QUIZ: what will be the first parameter of sum ? 525 | // self / &self / &mut self 526 | 527 | assert_eq!(total, 6); 528 | 529 | // Another kind of method is known as an iterator adaptor, 530 | // which can allow you to change iterators into different kinds of iterators. 531 | // You can chain multiple calls to iterator adaptors to perform complex actions 532 | // in a readable way. 533 | // But because all iterators are lazy, 534 | // you have to call one of the consuming adaptor methods 535 | // to get results from calls to iterator adaptors, as shown below. 536 | 537 | // However, the following code doesn't do anything since 538 | // the closure we specified never gets called. 539 | // The reason for this is that iterator adaptors are lazy, 540 | // and will only consume the iterator when needed. 541 | let v1: Vec = vec![1, 2, 3]; 542 | v1.iter().map(|x| { x + 1 }); 543 | 544 | // We can finish this example as shown below. 545 | let v2: Vec<_> = v1.iter().map(|x| { x + 1 }).collect(); 546 | 547 | assert_eq!(v2, vec![2, 3, 4]); 548 | } 549 | 550 | 551 | // We can now demonstrate a common use of closures 552 | // that capture their environment by using the filter iterator adaptor. 553 | // The filter method on an iterator takes a closure 554 | // that takes each item from the iterator and returns a Boolean. 555 | // If the closure returns true, 556 | // the value will be included in the iterator produced by filter. 557 | // If the closure returns false, 558 | // the value won’t be included in the resulting iterator. 559 | 560 | #[derive(PartialEq, Debug)] 561 | struct Shoe { 562 | size: u32, 563 | style: String, 564 | } 565 | 566 | // In the following example, we use filter with a closure that captures 567 | // the shoe_size variable from its environment 568 | // to iterate over a collection of Shoe struct instances. 569 | // It will return only shoes that are the specified size. 570 | fn shoes_in_size(shoes: Vec, shoe_size: u32) -> Vec { 571 | let f = |s : &Shoe| { s.size == shoe_size }; 572 | shoes.into_iter().filter(f).collect() 573 | } 574 | 575 | pub fn filters_by_size() { 576 | // create 3 shoes in a vec 577 | let shoes = vec![ 578 | Shoe { 579 | size: 10, 580 | style: String::from("sneaker"), 581 | }, 582 | Shoe { 583 | size: 13, 584 | style: String::from("sandal"), 585 | }, 586 | Shoe { 587 | size: 10, 588 | style: String::from("boot"), 589 | }, 590 | ]; 591 | 592 | // call the filter with param 10 593 | let in_my_size = shoes_in_size(shoes, 10); 594 | println!("filtered vec: {:?}", in_my_size ); 595 | } 596 | 597 | 598 | // Previously we've created iterators by calling iter, into_iter, or iter_mut on a vector. 599 | // You can create iterators from the other collection types 600 | // in the standard library, such as hash map. 601 | // You can also create iterators that do anything you want 602 | // by implementing the Iterator trait on your own types. 603 | // As previously mentioned, the only method you’re required to provide 604 | // a definition for is the next method. 605 | // 606 | struct Counter { 607 | count: u32, 608 | upperbound:u32 609 | } 610 | impl Counter { 611 | fn new() -> Counter { 612 | Counter { count: 0, upperbound:10 } 613 | } 614 | } 615 | // We can implement an iterator for this struct as shown below. 616 | impl Iterator for Counter { 617 | // we need to define what the Item type of the iterator is 618 | type Item = u32; 619 | 620 | // and next returns an option of that item 621 | fn next(&mut self) -> Option { 622 | if self.count < self.upperbound { 623 | self.count += 1; 624 | Some(self.count) 625 | } else { 626 | None 627 | } 628 | } 629 | } 630 | // We can use the iterator as shown below. 631 | pub fn calling_next_directly() { 632 | let mut counter = Counter::new(); 633 | 634 | // QUIZ: what will counter.next be? 635 | // 0 | 1 | Some(1) | Some(0) | None 636 | 637 | // 638 | assert_eq!(counter.next(), Some(1)); 639 | assert_eq!(counter.next(), Some(2)); 640 | assert_eq!(counter.next(), Some(3)); 641 | assert_eq!(counter.next(), Some(4)); 642 | assert_eq!(counter.next(), Some(5)); 643 | assert_eq!(counter.next(), None); 644 | 645 | // what are the differences between THIS iterator and the VEC one? 646 | 647 | } 648 | // 649 | // Note, that with the simple implementation of this next method, 650 | // we can use various other methods associated with the iterator trait. 651 | // 652 | pub fn using_other_iterator_trait_methods() { 653 | // QUIZ: group up- read up 654 | // skip , zip , map , filter , sum 655 | // explain what each thing does to its input and thus their output 656 | let sum : Vec = Counter::new() 657 | /* zip is an iterator that iterates over 2 other iterators, in this case 658 | Counter::new and Counter::new().skip 659 | */ 660 | .zip(Counter::new().skip(1)) 661 | /* multiply each element of the 2 iterators 662 | 0*1 | 1*2 | 2*3 | 3*4 | 4 * 5 663 | 0 | 2 | 6 | 12 | 20 664 | */ 665 | .map(|(a, b)| a * b) 666 | /*keep only those numbers in that can be divided by 3 667 | */ 668 | .filter(|x| x % 3 == 0) 669 | /*add all the numbers 670 | */ 671 | .collect() 672 | ; 673 | // println!("{}",sum); 674 | } 675 | 676 | 677 | // There are other methods implemented on iterators within rust 678 | // that are similar to one used in functional programming languages. 679 | // Some examples of these include 680 | // map, 681 | // fold, 682 | // sum, 683 | // filter, 684 | // zip, etc. 685 | // 686 | // The following example uses map, 687 | // The map() method applies a function to each element in an iterable 688 | // and returns the resulting iterable, of each iteration, to the next function. 689 | // 690 | pub fn examplefpiterators() { 691 | let vector = [1, 2, 3]; 692 | let result = vector.iter().map(|x| { x * 2 }).collect::>(); 693 | // another way to write the above statement 694 | /*let result: Vec = vector.iter().map(|x| x * 2).collect();*/ 695 | println!("After mapping: {:?}", result); 696 | 697 | // The following example uses sum, 698 | // which takes an iterator and generates Self from the elements by “summing up” the items. 699 | // 700 | let su: u32 = vec![1, 2, 3, 4, 5, 6].iter().sum(); 701 | // 702 | // The following example accomplishes the same thing using fold. 703 | // Folding is useful whenever you have a collection of something, 704 | // and want to produce a single value from it. 705 | // 706 | let sum: u32 = vec![1, 2, 3, 4, 5, 6].iter().fold(0, |mut summ, &val| { 707 | summ += val; 708 | summ 709 | }); 710 | // 711 | // The following example uses collect, which consumes the iterator 712 | // and collects the resulting values into a collection data type. 713 | // 714 | let a = [1, 2, 3]; 715 | let doubled: Vec = a.iter() 716 | .map(|&x| x * 2) 717 | .collect(); 718 | 719 | assert_eq!(vec![2, 4, 6], doubled); 720 | // 721 | // A longer list of these functions can be found 722 | // [here](https://doc.rust-lang.org/std/iter/trait.Iterator.html). 723 | } 724 | 725 | } -------------------------------------------------------------------------------- /src/classes/c13_maps.rs: -------------------------------------------------------------------------------- 1 | use std::num::ParseIntError; 2 | 3 | 4 | /// 5 | /// https://www.newline.co/@kkostov/the-rust-map-function-a-gateway-to-iterators--f991b22b 6 | 7 | // do you recall map from functional programming? 8 | // type of map: list a -> (a->b) -> list b 9 | // 10 | // in Rust, map works on iterators 11 | // because they're a general concept and all collections implement them 12 | // so it's easy to lift the notion from lists to hashmaps etc 13 | pub fn singlemap(){ 14 | let numbers = vec![3, 6, 9, 12]; 15 | let result: Vec = numbers 16 | .iter() 17 | .map(|n| n * 10) 18 | .collect(); 19 | println!("{:?}",result); 20 | } 21 | // what does map return then? another iterator! 22 | // because we can chain maps! 23 | pub fn twomaps(){ 24 | let numbers = vec![3, 6, 9, 12]; 25 | let result: Vec = numbers 26 | .iter() 27 | .map(|n| n * 10) 28 | .map(|n| n / 3) 29 | .collect(); 30 | println!("{:?}",result); 31 | } 32 | // notice the `.collect()` 33 | // it's there to convert the returned iterator into a Vec 34 | 35 | 36 | // map is LAZY 37 | pub fn lazymap_collect(){ 38 | let numbers = vec![3, 6, 9, 12]; 39 | let mut number_of_times = 0; 40 | let result: Vec = numbers 41 | .iter() 42 | .map(|n| { 43 | number_of_times += 1; 44 | return n * 10 45 | }) 46 | .collect(); 47 | println!("{:?}",result); 48 | println!("{}",number_of_times); 49 | } 50 | pub fn lazymap_nocollect(){ 51 | let numbers = vec![3, 6, 9, 12]; 52 | let mut number_of_times = 0; 53 | // notice the change in type here 54 | let result = numbers 55 | .iter() 56 | .map(|n| { 57 | number_of_times += 1; 58 | return n * 10 59 | }); 60 | println!("{:?}",result); 61 | println!("{}",number_of_times); 62 | } 63 | // iterator a -> (a->b) -> iterator b 64 | 65 | // QUIZ: what will this print? 66 | 67 | pub fn string_tolower(){ 68 | let words: Vec<&str> = vec!["Hello", "from", "Rust", "!"]; 69 | println!("Words before map: {:?}", words); 70 | let lowercased_words: Vec = words // using String as the return type of `to_lowercase` 71 | .iter() 72 | .map(|word| word.to_lowercase()) 73 | .collect(); 74 | println!("Words after map: {:?}", lowercased_words); 75 | println!("{:?}",words); 76 | } 77 | 78 | // pub fn maps_options(){ 79 | // let str_numbers: Vec<&str> = vec!["1", "2", "3", "I am not a number"]; 80 | // let numbers: Vec> = str_numbers 81 | // .iter() 82 | // // does not compile 83 | // .map(|str_number| str_number.parse::()) 84 | // // must use `flat_map` 85 | // // https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.flat_map 86 | // // .flat_map(|str_number| str_number.parse::()) 87 | // .collect(); 88 | // println!("{:?}",numbers); 89 | // } 90 | 91 | /// https://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html/ 92 | #[derive(Debug)] 93 | pub struct Node { 94 | content: i32, 95 | } 96 | 97 | impl Node { 98 | pub fn new(content: i32) -> Node { 99 | Node { content } 100 | } 101 | pub fn inc_content(&mut self){self.content+=1;} 102 | } 103 | 104 | pub fn mapsownership(){ 105 | let s1 = String::from("asd1"); 106 | let s2 = String::from("asd2"); 107 | let s3 = String::from("asd3"); 108 | let s4 = String::from("asd4"); 109 | let mut v : Vec = Vec::new(); 110 | v.push(s1); v.push(s2); v.push(s3); v.push(s4); 111 | let newv : Vec = v 112 | .iter() 113 | .map(|x| x.replace('a', "aa")) 114 | .collect(); 115 | println!("v: {:?}",v); 116 | println!("v: {:?}",newv); 117 | // using 'replace' returns a new string! no need for ownership transfer 118 | // so many similar examples are not informative 119 | 120 | let n1 = Node::new(10); 121 | let n2 = Node::new(20); 122 | let n23 = Node::new(23); 123 | let n4 = Node::new(40); 124 | let mut vv = vec![n1,n2,n4]; 125 | vv.push(n23); 126 | // pushing moves ownership: vv now owns the nodes 127 | let mut v2 : Vec = Vec::new(); 128 | println!("vv {:?}",vv); 129 | // let r : Vec<()> = vv 130 | // .iter_mut() 131 | // .map(|mut el| el.inc_content()) 132 | // .collect(); 133 | // println!("vv {:?}",vv); 134 | // println!("r {:?}",r); 135 | println!("v2 {:?}",v2); 136 | // but iter and iter_mut are still borrowing data, not owning it 137 | // in fact, here we can still print vv 138 | 139 | let rr : Vec<_> = vv 140 | // .iter() //cannot move 141 | .into_iter() // acquires ownership 142 | .map(|el : Node | 6 ) 143 | .collect(); 144 | // println!("vv {:?}",vv); // passed ownership of vv away 145 | println!("v2 {:?}",v2); 146 | println!("rr {:?}",rr); 147 | // to change ownership of data, use into_iter 148 | // see that we cannot1 print vv afterwards, vv is not the owner! 149 | } 150 | -------------------------------------------------------------------------------- /src/classes/c14_conc.rs: -------------------------------------------------------------------------------- 1 | /// This module presents these topics: 2 | /// threads 3 | /// message-passing and channels 4 | /// concurrency-related traits (Send, Sync) 5 | /// phantom types 6 | 7 | /// Material for this module: 8 | /// https://doc.rust-lang.org/book/ch16-00-concurrency.html 9 | /// https://doc.rust-lang.org/rust-by-example/generics/phantom.html 10 | 11 | // goal: overview of general notions and paradigm shift: 12 | // threads + communication 13 | 14 | // Terminology first: concurrency VS parallelism (VS distribution) 15 | // Concurrency is when two or more tasks can start, run, and complete in overlapping time periods. 16 | // It doesn't necessarily mean they'll ever both be running at the same instant. 17 | // For example, multitasking on a single-core machine. 18 | // Parallelism is when tasks literally run at the same time, e.g., on a multicore processor. 19 | // 20 | // OR : 21 | // Concurrency is two lines of customers ordering from a single cashier (lines take turns ordering); 22 | // Parallelism is two lines of customers ordering from two cashiers (each line gets its own cashier) 23 | // 24 | // Distribution: tasks run on physically separated nodes 25 | 26 | // In most current operating systems, a program code is run in a process, and the OS manages 27 | // multiple processes at once. Within your program, you can also have independent parts that 28 | // run simultaneously. The features that run these independent parts are called threads. 29 | // 30 | // Multi-threaded task can lead to the follwoing problems: 31 | // - Race conditions, where threads are accessing data or resources in an inconsistent order 32 | // - Deadlocks, where two threads are waiting for each other to finish using a resource 33 | // the other thread has, preventing both threads from continuing 34 | // - Bugs that happen only in certain situations and are hard to reproduce and fix reliably 35 | // 36 | // Rust std provides 1:1 threading, meaning one operating system thread per one language thread. 37 | // There are crates that provide M:N threading in rust, such as futures, 38 | // but we will focus on the theading methods in Rust std. 39 | 40 | use std::thread; 41 | use std::time::Duration; 42 | 43 | pub fn thread_spawning() { 44 | // QUIZ: what is the type of the argument of spawn ? 45 | let handle = thread::spawn(|| { 46 | for i in 1..10 { 47 | println!("hi number {} from the spawned thread!", i); 48 | thread::sleep(Duration::from_millis(1)); 49 | } 50 | }); 51 | for i in 1..5 { 52 | println!("hi number {} from the main thread!", i); 53 | thread::sleep(Duration::from_millis(1)); 54 | } 55 | // Q: what numbers do we expect to see printed? 56 | 57 | // 58 | // The threads will probably take turns, but that isn’t guaranteed: 59 | // it depends on how your operating system schedules the threads. 60 | // In this run, the main thread printed first, even though the print statement 61 | // from the spawned thread appears first in the code. And even though we told the 62 | // spawned thread to print until i is 9, it only got to 5 before the main thread shut down. 63 | // 64 | // We can fix the problem of the spawned thread not getting to run, 65 | // or not getting to run completely, by saving the return value of thread::spawn in a variable. 66 | // The return type of thread::spawn is JoinHandle. A JoinHandle is an owned value that, 67 | // when we call the join method on it, will wait for its thread to finish. 68 | // 69 | handle.join().unwrap(); 70 | } 71 | 72 | pub fn thread_test() { 73 | let v = vec![1, 2, 3]; 74 | 75 | // QUIZ: why do we need the move keyword here? 76 | // how is v passed? immutable borrow/mutable borrow/ownership 77 | let handle = thread::spawn(move || { 78 | println!("Here's a vector: {:?}", v); 79 | }); 80 | 81 | // drop(v); // oh no! 82 | handle.join().unwrap(); 83 | 84 | // Rust infers how to capture v, and because println! only needs a reference to v, 85 | // the closure tries to borrow v. However, there’s a problem: Rust can’t tell 86 | // how long the spawned thread will run, so it doesn’t know if the reference to v 87 | // will always be valid . So, we can use move to solve this problem. However, 88 | // this also means we cannot use v anymore after calling spawn! 89 | } 90 | 91 | 92 | /// message passing 93 | /// https://go.dev/doc/effective_go#concurrency 94 | /// Do not communicate by sharing memory; instead, share memory by communicating. 95 | 96 | // sharing memory across threads can cause many issues, 97 | // other languages have other means for threads communication: 98 | // pipes, signals, etc 99 | 100 | // channels are the concurrency abstraction, much like functions are the sequential one 101 | 102 | // We create a new channel using the mpsc::channel function; mpsc stands for multiple producer, single consumer. 103 | // In short, the way Rust’s standard library implements channels means a channel can have 104 | // multiple sending ends that produce values 105 | // but only one receiving end that consumes those values. 106 | // The mpsc::channel function returns a tuple, the first element of which is the sending end 107 | // and the second element is the receiving end. 108 | 109 | use std::sync::mpsc; 110 | 111 | pub fn channel_example() { 112 | // (transmitter, receiver) 113 | let (tx, rx) = mpsc::channel(); 114 | 115 | // clone the transmitter so that each child thread gets one 116 | let tx1 = tx.clone(); 117 | 118 | thread::spawn(move || { 119 | let vals = vec![ 120 | String::from("one rubber duck in river 1"), 121 | String::from("two rubber ducks in river 1"), 122 | String::from("three rubber ducks in river 1"), 123 | String::from("four rubber ducks in river 1"), 124 | ]; 125 | for val in vals { 126 | tx.send(val).unwrap(); // send() returns a Result 127 | thread::sleep(Duration::from_secs(1)); 128 | } 129 | // send takes the ownership of val 130 | // println!("{}",val); ERROR! 131 | }); 132 | 133 | thread::spawn(move || { 134 | let vals = vec![ 135 | String::from("one rubber duck in river 2"), 136 | String::from("two rubber ducks in river 2"), 137 | String::from("three rubber ducks in river 2"), 138 | String::from("four rubber ducks in river 2"), 139 | ]; 140 | for val in vals { 141 | tx1.send(val).unwrap(); 142 | thread::sleep(Duration::from_secs(1)); 143 | } 144 | }); 145 | // rx blocks the main thread! it implements the Iterator trait 146 | for received in rx { 147 | // the main thread listens on rx and receives ducks from threads 1 and 2 148 | println!("Got: {}", received); 149 | } 150 | } 151 | 152 | // Channels in any programming language are similar to single ownership, 153 | // because once you transfer a value down a channel, 154 | // you should no longer use that value. 155 | // Shared memory concurrency is like multiple ownership: 156 | // multiple threads can access the same memory location at the same time. 157 | 158 | // The secret is Mutex smart pointer. Mutex is an abbreviation for mutual exclusion, 159 | // as in, a mutex allows only one thread to access some data at any given time. 160 | // Like RefCell, Mutex provides interior mutability. 161 | 162 | use std::sync::Mutex; 163 | 164 | pub fn mutex_example() { 165 | let m = Mutex::new(5); 166 | 167 | { 168 | let mut num = m.lock().unwrap(); 169 | *num = 6; 170 | } // unlock 171 | 172 | println!("m = {:?}", m); 173 | } 174 | 175 | // As with many types, we create a Mutex using the associated function new. 176 | // To access the data inside the mutex, we use the lock method to acquire the lock. 177 | // This call will block the current thread so it can’t do any work until it’s our turn to have the lock. 178 | // The call to lock would fail if another thread holding the lock panicked. 179 | // In that case, no one would ever be able to get the lock, so we’ve chosen to unwrap 180 | // and have this thread panic if we’re in that situation. 181 | // After we’ve acquired the lock, we can treat the return value, named num in this case, 182 | // as a mutable reference to the data inside. The type system ensures that we acquire a 183 | // lock before using the value in m: Mutex is not an i32, so we must acquire the 184 | // lock to be able to use the i32 value. We can’t forget; the type system won’t let us 185 | // access the inner i32 otherwise. 186 | // As you might suspect, Mutex is a smart pointer. More accurately, the call to lock 187 | // returns a smart pointer called MutexGuard, wrapped in a LockResult that we handled 188 | // with the call to unwrap. The MutexGuard smart pointer implements Deref to point at our 189 | // inner data; the smart pointer also has a Drop implementation that releases the lock 190 | // automatically when a MutexGuard goes out of scope. As a result, we don’t risk forgetting 191 | // to release the lock and blocking the mutex from being used by other threads because the 192 | // lock release happens automatically. 193 | // 194 | // After dropping the lock, we can print the mutex value and see that we were able to change 195 | // the inner i32 to 6. 196 | // 197 | // As move will take the ownership of Mutex if we use it directly in spawn. 198 | // So if we want to share Mutex between multiple threads, we need to give the ownership 199 | // of the Mutex value to every thread. To do this, we use the atomic reference counting 200 | // smart pointer Arc, where atomic means it’s an atomically reference counted type 201 | // that can be sent across threads. 202 | 203 | use std::sync::{Arc,}; 204 | pub fn arc_mutex() { 205 | let counter = Arc::new(Mutex::new(0)); 206 | let mut handles = vec![]; 207 | 208 | for _ in 0..10 { 209 | let counter = Arc::clone(&counter); 210 | let handle = thread::spawn(move || { 211 | let mut num = counter.lock().unwrap(); 212 | *num += 1; 213 | }); 214 | handles.push(handle); 215 | } 216 | for handle in handles { 217 | handle.join().unwrap(); 218 | } 219 | println!("Result: {}", *counter.lock().unwrap()); 220 | } 221 | 222 | // The Send marker trait indicates that ownership of values of the type implementing 223 | // Send can be transferred between threads. Almost every Rust type is Send, 224 | // but there are some exceptions, including Rc 225 | // The Sync marker trait indicates that it is safe for the type implementing Sync 226 | // to be referenced from multiple threads. Primitive types are Sync, 227 | // and types composed entirely of types that are Sync are also Sync. Rc is also not Sync. 228 | // Send and Sync are marker traits. 229 | // Marker types don’t have any methods to implement. 230 | // They’re just useful for enforcing invariants related to concurrency 231 | 232 | 233 | 234 | // next: advanced classes: will + recordings 235 | // -------------------------------------------------------------------------------- /src/classes/c99_QA.rs: -------------------------------------------------------------------------------- 1 | // https://www.theregister.com/2022/11/11/nsa_urges_orgs_to_use/ 2 | 3 | 4 | /// rust's management of Crates: 5 | /// - mod component: 6 | /// - in the main (for specifying at the crate-level what modules to be considered) 7 | /// - in a mod.rs (pub mod ... ) for telling cargo that the folder contains these modules 8 | /// with these names and these visibility modifiers 9 | /// - in a file: to create a hierarchy of modules and namespaces 10 | /// - what is a path : use crate:: // super:: 11 | 12 | 13 | /// lifetimes 14 | 15 | pub struct Inner { 16 | value : i32 17 | } 18 | impl Inner { 19 | pub fn new()-> Inner { 20 | Inner {value:0} 21 | } 22 | } 23 | /// let's define a struct Inner, which we can't just call Goods for obv reasons 24 | pub struct Container<'a>{ 25 | content: &'a Inner, 26 | data: i32 27 | } 28 | /// let's define a struct Container that takes a pointer to an Inner 29 | /// the Inner could be owned by sth else, and it makes sense to pass a pointer 30 | /// we need to specify the lifetime to ensure the Inner lives long enough, 31 | /// so the pointer inside Container is valid for the lifetime of Container 32 | /// So the lifetime of the Inner must be at least as long as the lifetime of the Container 33 | impl<'a> Container<'a> { 34 | pub fn new(a:&Inner) -> Container { 35 | Container { content:a,data:0} 36 | } 37 | // let's define a methhod for changing the content of a container 38 | pub fn addinner(&mut self, a:&'a Inner) { 39 | self.content = a; 40 | } 41 | } 42 | 43 | // now let's simulate other external functions that invoke the addinner 44 | // this is your other code using the Container API 45 | 46 | /// the function below is incorrect: why? 47 | // pub fn modder(c: &mut Container){ // ----| 48 | // let a = Inner::new(); // 49 | // c.addinner(&a); // 50 | // } // ----| 51 | 52 | // 53 | /// The lifetime of a is shorter than the Container's! it's been allocated after! 54 | 55 | /// To fix this, we need to pass the lifetime of Inner and make sure it matches what Container wants 56 | pub fn alsogoodmodder<'a>(c:&mut Container<'a>, a:&'a Inner){ 57 | c.addinner(a); 58 | } 59 | pub fn goodmodder<'a, 'b>(c:&'b mut Container<'a>, a:&'a Inner){ 60 | c.addinner(a); 61 | } 62 | 63 | pub mod test{ 64 | // use crate::basedir::c99_QA::*; 65 | // use crate::lifetimes::lt::{*}; 66 | 67 | pub fn main(){ 68 | // let mut a = Inner::new(); 69 | // let mut b = Container::new(&a); 70 | // b.addinner(&a); 71 | // // modder(&mut b); 72 | // 73 | // goodmodder(&mut b, &a); 74 | } 75 | } 76 | 77 | 78 | 79 | /// 80 | pub mod traitqa{ 81 | use std::ops::{Add, Deref, DerefMut}; 82 | 83 | pub struct S1{ 84 | f1:i32 85 | } 86 | pub struct S2{ 87 | f2:bool 88 | } 89 | pub trait Addable{ 90 | fn get_i32(&self)-> i32; 91 | fn add(&mut self, o:&dyn Addable); 92 | } 93 | impl Addable for S1{ 94 | fn get_i32(&self) -> i32 { 95 | self.f1 96 | } 97 | fn add(&mut self, o: &dyn Addable) { 98 | self.f1+=o.get_i32(); 99 | } 100 | } 101 | impl Addable for S2{ 102 | fn get_i32(&self) -> i32 { 103 | if self.f2 {0} else {1} 104 | } 105 | fn add(&mut self, o: &dyn Addable) { 106 | let mut tmp = false; 107 | if o.get_i32() == 0 {tmp = true;}; 108 | self.f2 && tmp; 109 | } 110 | } 111 | 112 | pub fn testit(){ 113 | let mut s1 = S1{f1:0}; 114 | let mut s2 = S2{f2:false}; 115 | let mut s3 = S2{f2:true}; 116 | let mut s4 = S1{f1:10}; 117 | 118 | let mut s5 = S1{f1:100}; 119 | 120 | let mut v1 : Vec<&dyn Addable> = vec![(&s1),(&s2),(&s3),(&s4)]; 121 | let mut v2 : Vec<&dyn Addable> = vec![(&s2),(&s3),(&s4),(&s1)]; 122 | for el1 in v1.iter() { 123 | println!("i32 {}", el1.get_i32()); 124 | } 125 | s1.add(&s2); 126 | 127 | } 128 | } -------------------------------------------------------------------------------- /src/classes/mod.rs: -------------------------------------------------------------------------------- 1 | // every module needs to contain a file 'mod.rs', i.e., this file 2 | 3 | // Below is a list of those files inside this directory that are externally visible 4 | pub mod c01_basic; 5 | pub mod c01_std; 6 | pub mod c02_ownership; 7 | pub mod c03_enums; 8 | pub mod c04_structs; 9 | pub mod c04_structshelper; 10 | pub mod c05_modules; 11 | 12 | pub mod c07_generics; 13 | pub mod c06_testing; 14 | pub mod c08_lifetimes; 15 | pub mod c13_maps; 16 | pub mod c09_traits; 17 | pub mod c10_OOP; 18 | pub mod c11_heap; 19 | pub mod c12_fp; 20 | pub mod c99_QA; 21 | pub mod c14_conc; -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | #![allow(dead_code)] 3 | #![allow(unused_parens)] 4 | #![allow(dead_code)] 5 | #![allow(non_camel_case_types)] 6 | #![allow(unused_imports)] 7 | mod classes; 8 | 9 | use crate::classes as basedir; 10 | use classes::c01_basic as c1; 11 | use classes::c01_std; 12 | use classes::c02_ownership as c2; 13 | use classes::c03_enums as c3; 14 | use classes::c04_structs as c4; 15 | use classes::c04_structshelper as c4b; 16 | use classes::c07_generics as c7; 17 | use classes::c08_lifetimes as c8; 18 | use classes::c09_traits as c9; 19 | use classes::c10_OOP as c10; 20 | pub fn main() { 21 | // // // from c01_basic 22 | // c1::var_ass_mut(); 23 | // c1::vals_types(); 24 | // c1::expressions(); 25 | // 26 | // // from c01_std 27 | // c01_std::strings(); 28 | // c01_std::vec(); 29 | // c01_std::hashmap() 30 | 31 | // c2::ownership(); 32 | // c2::ownership_for_functions(); 33 | // c2::refs_and_borrowing(); 34 | // c2::slices(); 35 | // c2::ownership_and_compound(); 36 | //s 37 | // // from c03_enums 38 | // c3::enum_usage(); 39 | // c3::option(); 40 | // c3::patternmatching(); 41 | // c3::errors(); 42 | // c3::testqm(); 43 | // 44 | // // from c04_structs 45 | // c4::struct_usage(); 46 | // c4::struct_printing(); 47 | // c4b::_showcase_access(); 48 | // c4::struct_impl(); 49 | // c4::ownstructs(); 50 | 51 | // c3::testqm(); 52 | // let r = c3::readfilecontent(); 53 | // if r.is_err(){ 54 | // println!("Error {:?}", r.unwrap_err()); 55 | // } 56 | // c4::ownstructs(); 57 | 58 | // 59 | // // from c05_modules 60 | // c5::externalcall(); 61 | // c5::external_registry_call(); 62 | // 63 | // // open c06_testing 64 | 65 | // 66 | // c7::struct_generic(); 67 | // c7::generics_example(); 68 | // c7::explicit_type(); 69 | 70 | // 71 | // c8::lifetime_test(); 72 | // c8::uselongest(); 73 | // c8::nll_example(); 74 | // c8::main(); 75 | // 76 | // // c09_traitspoly 77 | // c9::traitexample(); 78 | // c9::example_notify(); 79 | // c9::animals_example(); 80 | // c9::example_supertraits(); 81 | // 82 | // // c10_oop 83 | c10::example_oop1(); 84 | // c10::example_animals_oop(); 85 | // c10::example_multiple_traits(); 86 | // 87 | // // c11_heap 88 | // c11::example_box(); 89 | // c11::example_box_long(); 90 | // c11::recursivetypes(); 91 | // c11::example_smart1(); 92 | // c11::example_drop(); 93 | // c11::example_rc(); 94 | // c11::implitictderef(); 95 | // c11::arc(); 96 | // c11::refcell_usage(); 97 | // c11::refcell_usage_2(); 98 | // c11::tests::it_sends_an_over_75_percent_warning_message(); 99 | // c11::workingtests::it_sends_an_over_75_percent_warning_message(); 100 | // c11::rc_plus_refcell::examplepcrefcell(); 101 | // c11::overflow::exampleoverflow(); 102 | // c11::graphexample(); 103 | // c11::cellexamplee(); 104 | // c11::rcwithcellexample(); 105 | // c11::par::arcmutex(); 106 | 107 | // // c12_fp 108 | // c12::closures::closuresexample(); 109 | // c12::closures::capturingexample(); 110 | // c12::closures::fntypes(); 111 | // c12::closures::closures_output(); 112 | // c12::closures::fprules(); 113 | // c12::iterators::iteratorexample(); 114 | // c12::iterators::filters_by_size(); 115 | // c12::iterators::examplefpiterators(); 116 | // c12::iterators::calling_next_directly(); 117 | // c12::iterators::using_other_iterator_trait_methods(); 118 | 119 | // cqa::traitqa::testit(); 120 | 121 | // c14::thread_spawning(); 122 | // c14::thread_test(); 123 | // c14::channel_example(); 124 | // c14::mutex_example(); 125 | // c14::arc_mutex(); 126 | 127 | } 128 | 129 | // use std::marker::PointeeSized; 130 | 131 | #[derive(Debug)] 132 | enum C{ 133 | C1(i32,i32), 134 | C2(bool,String) 135 | } 136 | #[derive(Debug)] 137 | struct D{ 138 | c : C, 139 | s : String 140 | } 141 | impl D{ 142 | pub fn new() -> D{ 143 | D{ c: C::C1(0,0), s: "".to_string() } 144 | } 145 | pub fn new_with_C(cc : C) -> D{ 146 | let content = match &cc { 147 | C::C1(_, _) => String::from("not there"), 148 | C::C2(_, s) => s.clone() 149 | }; 150 | D{ 151 | c : cc, 152 | s: content 153 | } 154 | } 155 | pub fn larger(&self) -> usize { 156 | let con = match &self.c { 157 | C::C1(_,_) => 0, 158 | C::C2(_,ss) => ss.len(), 159 | }; 160 | let l = self.s.len(); 161 | return if l > con {l} else {con}; 162 | } 163 | } 164 | 165 | // impl fmt::Display for D { 166 | // fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 167 | // write!(f, "D: {} with {:?}", self.s, self.c) 168 | // } 169 | // } 170 | // pub fn main () { 171 | // let c1 = C::C1(0,1); 172 | // let c2 = C::C2(true, String::from("no way jose")); 173 | // println!("gotten {:?}",D::new()); 174 | // let d1 = D::new_with_C(c1); 175 | // println!("dbg {:?}",d1); 176 | // println!("fmt {}",d1); 177 | // let d2 = D::new_with_C(c2); 178 | // println!("dbg {:?}",d2); 179 | // println!("fmt {}",d2); 180 | // println!("larger {}",d2.larger()); 181 | // println!("larger {}",d1.larger()); 182 | // } 183 | -------------------------------------------------------------------------------- /src/primers/FP.md: -------------------------------------------------------------------------------- 1 | FP: https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf 2 | 3 | Programming is about 4 | ABSTRACTIONS 5 | -> registers? 6 | -> jumps/gotos? 7 | 8 | FP key abstractions: 9 | - immutability: known 10 | - no assignment (no rebinding) 11 | - no side-effects 12 | - modularity // divide et impera 13 | - algebraic data types 14 | - functions 15 | - higher-order 16 | - pattern-matching: known 17 | 18 | FP benefits: 19 | - small code 20 | - verifiable 21 | - reasonable in isolation 22 | - reusable 23 | 24 | Algebraic data types: 25 | - Composite types 26 | - defined inductively (base + inductive) 27 | - ex: list, pairs 28 | 29 | 30 | Tree :: Node | Tree + Tree 31 | 32 | Lists in ML: 33 | 34 | [] // nil 35 | | (h :: t ) // cons head tail 36 | 37 | So: 38 | 39 | (2::4::[]) :: (1::3::5::[]) :: [] 40 | 41 | Dealing with ADTs: induction --> recursion 42 | 43 | FP typically provides datatypes (disjoint unions) 44 | Example (Binary Trees) 45 | 46 | datatype ’a bintr = LEAF of ’a 47 | | NODE of ’a bintr*’a bintr; 48 | 49 | datatype ’a bintr = LEAF of ’a 50 | | NODE of ’a bintr * ’a bintr 51 | 52 | They can have any number of variants 53 | - does the name suggest anything? 54 | 55 | Example creating a new tree: 56 | 57 | val tree = NODE (NODE(LEAF 1,LEAF 4),LEAF 7); 58 | val tree = NODE(NODE(LEAF 1,LEAF 4),LEAF 7) : int bintDavid Toman (University of Waterloo) Standard ML 15 / 21 59 | 60 | Functions on trees: use pattern matching 61 | 62 | 63 | Functions: 64 | - define them : known 65 | - call them : known 66 | - pass them (future classes in rust) 67 | - compose them: 68 | - q: given `f : c → a` and `g : b → c`, what is the type of `f g` ? 69 | - typed, polymorphic, 70 | - what kinds of polymorphism exist? 71 | - higher-order 72 | - what does this mean? 73 | 74 | examples: 75 | 76 | fun hd [] = raise Empty 77 | | hd (h::t) = h; 78 | 79 | fun last [] = raise Empty 80 | | last [x] = x 81 | | last (h::t) = last t; 82 | 83 | fun length [] = 0 84 | | length (h::t) = 1 + length t; 85 | 86 | Tail Recursion! 87 | - often achieved via accumulators 88 | - optimisable code 89 | 90 | 91 | fun length l = length_acc l 0 92 | 93 | fun lenght_acc [] a = a 94 | | length_Acc (h ::t ) a = length_acc t, a+1 95 | 96 | lenght_acc (1 ::2 :: [], 0) = 97 | lenght_acc ( 2::[], 1) = 98 | lenght_acc ( [], 2) = 2 99 | 100 | length ( 1::2::[]) = 101 | 1 + length ( 2::[]) 102 | 1 + 1+ length([]) 103 | 1 + 1 + 0 104 | 105 | Higher-order (+ polymorphism): 106 | 107 | fun map f [] = [] 108 | | map f (h::t) = (f h)::(map f t); 109 | 110 | What is its type? 111 | 112 | val map = fn : (’a -> ’b) -> (’a list -> ’b list) 113 | 114 | Example: 115 | 116 | map (fn x=> x+1) 1::2::3::[]; 117 | 118 | Other examples: foldr 119 | 120 | ∀'a, 'b. 'a list -> b -> (a -> b -> b) -> b --------------------------------------------------------------------------------