├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── src └── main.rs └── tests └── integration_tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "learning-rust" 3 | version = "0.1.0" 4 | 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "learning-rust" 3 | version = "0.1.0" 4 | authors = ["Mathieu Desrochers "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fmt::Debug; 3 | use std::hash::Hash; 4 | 5 | fn ownership_move(_x: Vec) {} 6 | 7 | fn ownership() { 8 | // each value has a single owner 9 | 10 | // assignation moves ownership 11 | // x1 is left uninitialized 12 | let x1: Vec = Vec::new(); 13 | let _y1 = x1; 14 | // COMPILE ERROR: let z1 = x1; 15 | 16 | // passing a parameter also moves ownership 17 | // x2 is left uninitialized 18 | let x2: Vec = Vec::new(); 19 | ownership_move(x2); 20 | // COMPILE ERROR: let z2 = x2; 21 | 22 | // copiable types are an exception 23 | let x3 = 1; 24 | let _y3 = x3; 25 | let _z3 = x3; 26 | 27 | // cannot move value out of its owner 28 | let mut x4 = Vec::new(); 29 | x4.push("a".to_string()); 30 | x4.push("b".to_string()); 31 | // COMPILE ERROR: let y4 = x4[0]; 32 | 33 | // can move value out of Option though 34 | // x5[0] is left as None 35 | let mut x5 = Vec::new(); 36 | x5.push(Some("a".to_string())); 37 | x5.push(Some("b".to_string())); 38 | let _y5 = x5[0].take(); 39 | 40 | // loops move ownership of a 41 | // container and their elements 42 | let mut x6 = Vec::new(); 43 | x6.push(Some("a".to_string())); 44 | x6.push(Some("b".to_string())); 45 | for _y6 in x6 {} 46 | // COMPILE ERROR: let _z6 = x6; 47 | // COMPILE ERROR: let _z6 = x6[0]; 48 | } 49 | 50 | fn references_shared(x: &HashMap) { 51 | // loops on references do not move ownership 52 | // let _y = x will compile 53 | for (_code, _name) in x {} 54 | } 55 | 56 | fn references_mutable(_x: &mut HashMap) {} 57 | 58 | fn references() { 59 | // values can be shared with references 60 | // without moving ownership 61 | 62 | // passing a reference parameter does not move ownership 63 | let mut x1: HashMap = HashMap::new(); 64 | x1.insert("LAX".to_string(), "Los Angeles".to_string()); 65 | x1.insert("YUL".to_string(), "Montréal".to_string()); 66 | references_shared(&x1); 67 | references_mutable(&mut x1); 68 | 69 | // all good 70 | let _y1 = x1; 71 | 72 | // the dot operator implicitly deferences 73 | // so do arithmetic and comparaison operators 74 | struct Airplane { 75 | callcode: String, 76 | _color: String, 77 | }; 78 | let x2 = Airplane { 79 | callcode: "Bandit".to_string(), 80 | _color: "Black".to_string(), 81 | }; 82 | let x2_r = &x2; 83 | 84 | // these are the same 85 | // this is great because the same code 86 | // works on values and on references 87 | let _y2_a = &((*x2_r).callcode); 88 | let _y2_b = &(x2.callcode); 89 | let _y2_c = &(x2_r.callcode); 90 | 91 | // the dot operator implicitly gets a reference 92 | // to the left operand if needed 93 | let mut x3 = vec!["B".to_string(), "A".to_string()]; 94 | 95 | // these are the same 96 | (&mut x3).sort(); 97 | x3.sort(); 98 | 99 | // can get reference to any expression 100 | // it gets an anonymous variable 101 | let x4 = &(1 + 2); 102 | let _y4 = x4 + &3; 103 | } 104 | 105 | fn lifetimes_basics() { 106 | // references must have shorter lifetimes 107 | // than the values they point to 108 | let _reference: &i32; 109 | { 110 | let _value = 1; 111 | // COMPILE ERROR: _reference = &_value; 112 | } 113 | } 114 | 115 | // lifetimes can be declared on functions 116 | // here is what is implied when nothing is spelled out 117 | fn _lifetimes_explicit<'a, 'b, 'c>(_x: &'a Vec, _y: &'b i32) -> &'c i32 { 118 | return &0; 119 | } 120 | 121 | // when a function returns a reference 122 | // and takes only one reference parameter 123 | // rust assumes they have the same lifetimes 124 | fn _lifetimes_explicit_simple_case<'a>(x: &'a Vec) -> &'a i32 { 125 | return &x[0]; 126 | } 127 | 128 | // this declaration limits the lifetime of the function's result 129 | // to the lifetime of its first parameter 130 | fn lifetimes_bound<'a, 'b>(x: &'a Vec, y: &'b usize) -> &'a i32 { 131 | return &x[*y]; 132 | } 133 | 134 | fn lifetimes() { 135 | lifetimes_basics(); 136 | 137 | // result cannot live longer than vector 138 | // since it points to one of vector's elements 139 | // we expressed this with 'a 140 | let vector = vec![1, 2, 3]; 141 | let _result; 142 | { 143 | // index must live only for the function call 144 | // we do not care otherwise 145 | let index: usize = 0; 146 | _result = lifetimes_bound(&vector, &index); 147 | } 148 | } 149 | 150 | fn lifetimes_structs() { 151 | // lifetimes must be declared inside structs 152 | // this limits the lifetime of the struct 153 | // to the lifetimes of its references 154 | struct Piano<'a> { 155 | _keys: &'a i32, 156 | }; 157 | 158 | // piano cannot live longer than keys 159 | // since it has a reference to keys 160 | // we expressed this with 'a 161 | let keys = 64; 162 | let _piano = Piano { _keys: &keys }; 163 | } 164 | 165 | fn mutability() { 166 | // values are immutable by default 167 | let _x1 = 25; 168 | // COMPILE ERROR: _x1 = 26; 169 | 170 | // immutability extends inside the variable 171 | let _x2 = vec![1, 2, 3]; 172 | // COMPILE ERROR: _x2.push(4); 173 | 174 | // tree structure to 175 | // illustrate next points 176 | struct Leaf { 177 | _value: i32, 178 | }; 179 | struct Branch { 180 | left: Leaf, 181 | right: Leaf, 182 | }; 183 | struct Root { 184 | left: Branch, 185 | right: Branch, 186 | }; 187 | 188 | let mut root = Root { 189 | left: Branch { 190 | left: Leaf { _value: 1 }, 191 | right: Leaf { _value: 2 }, 192 | }, 193 | right: Branch { 194 | left: Leaf { _value: 3 }, 195 | right: Leaf { _value: 4 }, 196 | }, 197 | }; 198 | 199 | // multiple read references are allowed 200 | // inside the same ownership tree 201 | { 202 | let _root_left = &root.left; 203 | let _root_left_left = &root.left.left; 204 | } 205 | 206 | // a read reference in the tree makes the children 207 | // and the ancestors immutable 208 | // we looking; no touchy 209 | { 210 | let _root_left = &root.left; 211 | 212 | // COMPILE ERRORS: 213 | // let _root_left_left = &mut root.left.left; 214 | // let _root = &mut root; 215 | 216 | // other parts of the tree are free game 217 | let _root_right = &mut root.right; 218 | } 219 | 220 | // a mutable reference in the tree makes the children 221 | // accessible only through that reference 222 | // and the ancestors inaccessible 223 | // we touchy, no looking 224 | { 225 | let root_left = &mut root.left; 226 | 227 | // COMPILE ERRORS: 228 | // let _root_left_left = &root.left.left; 229 | // let _root = &root; 230 | 231 | // other parts of the tree are free game 232 | let _root_right = &root.right; 233 | 234 | // children are accessible through 235 | // the mutable reference 236 | let _root_left_left = &root_left.left; 237 | let _root_left_right = &mut root_left.right; 238 | } 239 | } 240 | 241 | fn errors_success() -> Result { 242 | Ok(1) 243 | } 244 | 245 | fn _errors_failure() -> Result { 246 | Err("Uh oh".to_string()) 247 | } 248 | 249 | fn errors() { 250 | // results must be consumed 251 | // errors_success() will generate a warning 252 | let _x1 = match errors_success() { 253 | Ok(value) => value, 254 | Err(error) => panic!("{}", error), 255 | }; 256 | 257 | // this often is reduced to one 258 | // character using the ? operator 259 | fn propagation() -> Result { 260 | // either gets the success value 261 | // or propagates the error to the caller 262 | let y = errors_success()?; 263 | Ok(y + 1) 264 | }; 265 | 266 | // ignore a result 267 | let _ = propagation(); 268 | 269 | // cause a panic on error 270 | // better be real sure 271 | let _x2 = errors_success().unwrap(); 272 | let _x3 = errors_success().expect("Ugh"); 273 | } 274 | 275 | fn structs() { 276 | // named-field 277 | #[allow(dead_code)] 278 | struct Pizza { 279 | size: u8, 280 | with_bacon: bool, 281 | } 282 | 283 | let small = Pizza { 284 | size: 8, 285 | with_bacon: true, 286 | }; 287 | 288 | // populate fields from local variables 289 | // having the same name 290 | fn _medium(with_bacon: bool) -> Pizza { 291 | Pizza { 292 | size: 12, 293 | with_bacon, 294 | } 295 | } 296 | 297 | // populate fields from another instance 298 | let _large = Pizza { size: 16, ..small }; 299 | 300 | // tuple-like 301 | struct RgbColor(u8, u8, u8); 302 | let black = RgbColor(0, 0, 0); 303 | let _red = black.0; 304 | let _green = black.1; 305 | let _blue = black.2; 306 | 307 | // unit-like 308 | struct Square; 309 | let _square = Square; 310 | } 311 | 312 | fn associated_functions() { 313 | struct _Dog { 314 | is_good_boy: bool, 315 | } 316 | 317 | impl _Dog { 318 | fn _bark(&self) -> String { 319 | "ruf".to_string() 320 | } 321 | } 322 | } 323 | 324 | fn enums() { 325 | #[allow(dead_code)] 326 | enum Speed { 327 | Slow, 328 | TooFast, 329 | } 330 | 331 | // with tuple data 332 | #[allow(dead_code)] 333 | enum Shape { 334 | Circle(u32), 335 | Rectangle(u32, u32), 336 | } 337 | 338 | // with struct data 339 | #[allow(dead_code)] 340 | enum Monster { 341 | Rat { rabid: bool }, 342 | Vampire { blood_thristy: bool, bat_form: bool }, 343 | } 344 | 345 | // matching patterns 346 | let x1 = Speed::Slow; 347 | let _y1 = match x1 { 348 | Speed::Slow => "All good", 349 | Speed::TooFast => "Slow down you crazy", 350 | }; 351 | 352 | // with multiple values 353 | let x2 = 100; 354 | let _y2 = match x2 { 355 | 0...100 => "Small", 356 | 101 | 102 | 103 => "Medium", 357 | _ => "Big", 358 | }; 359 | 360 | // with guards 361 | let x3 = Shape::Rectangle(10, 20); 362 | let _y3 = match x3 { 363 | Shape::Rectangle(h, _) if h > 100 => "You a tall rectangle", 364 | Shape::Rectangle(_, _) => "You an ok rectangle", 365 | _ => "You no rectangle at all", 366 | }; 367 | 368 | // with fields 369 | let x4 = Monster::Vampire { 370 | blood_thristy: true, 371 | bat_form: false, 372 | }; 373 | let _y4 = match x4 { 374 | Monster::Rat { .. } => "Squishy", 375 | Monster::Vampire { 376 | blood_thristy: true, 377 | .. 378 | } => "We are sooo done...", 379 | Monster::Vampire { .. } => "Keep calm", 380 | }; 381 | 382 | // with borrowed references 383 | let _z4 = match x4 { 384 | Monster::Rat { ref rabid } => *rabid, 385 | _ => false, 386 | }; 387 | 388 | // with no unpacking 389 | let x5 = Shape::Rectangle(10, 20); 390 | let _y5 = match x5 { 391 | circle @ Shape::Circle(_) => circle, 392 | rectangle @ Shape::Rectangle(_, _) => rectangle, 393 | }; 394 | 395 | // testing a single pattern 396 | if let Shape::Circle(r) = x3 { 397 | let _area = 3.14 * (r as f32) * (r as f32); 398 | } 399 | } 400 | 401 | fn traits() { 402 | trait Eatable { 403 | fn eat_it(&self); 404 | 405 | // provide default implementation 406 | fn chump_it(&self, _speed: u8) { 407 | self.eat_it(); 408 | } 409 | } 410 | 411 | // adding traits to types 412 | impl Eatable for i32 { 413 | fn eat_it(&self) {} 414 | } 415 | 416 | impl Eatable for f64 { 417 | fn eat_it(&self) {} 418 | } 419 | 420 | // trait objects 421 | let x1: &Eatable = &12; 422 | fn have_quick_snack(_eatable: &Eatable) {} 423 | have_quick_snack(x1); 424 | 425 | // trait objects add runtime overhead 426 | // the type of eatable could be i32 or f64 427 | // it is unknown a compile time for public functions 428 | 429 | // but traits can be mixed and matched inside boxes 430 | let mut x2: Vec> = Vec::new(); 431 | x2.push(Box::new(12)); 432 | x2.push(Box::new(3.456)); 433 | 434 | // fully qualified trait method 435 | Eatable::eat_it(&9.999); 436 | 437 | // generics can be bound to traits 438 | fn have_lunch(_eatables: Vec) {} 439 | have_lunch(vec![1, 6, 2]); 440 | 441 | // generics make the compiled code bigger 442 | // different versions of have_lunch are compiled 443 | // one for each E used by the program 444 | 445 | // bounds can be a combinaison of traits 446 | fn _eat_debugable(_eatables: Vec) {} 447 | fn _eat_hashable(_eatables: Vec) 448 | where 449 | E: Eatable + Hash, 450 | { 451 | } 452 | } 453 | 454 | fn closures() { 455 | // a closure captures the variables it refers to 456 | let x1 = 1; 457 | let y1 = 2; 458 | let _z1a = |value: i32| -> i32 { x1 + y1 + value }; 459 | let _z1b = |value: i32| -> i32 { x1 * value }; 460 | 461 | // this gives each closure a different type 462 | // for the first closure we get something 463 | // logically similar to 464 | struct _Z1Struct<'a> { 465 | x1: &'a i32, 466 | y1: &'a i32, 467 | } 468 | 469 | // default captures are done by reference 470 | // the first closure must not outlive x1 nor y1 471 | 472 | // a common tait between all closures 473 | // capturing variables by reference is Fn 474 | let mut closures: Vec<&Fn(i32) -> i32> = Vec::new(); 475 | closures.push(&_z1a); 476 | closures.push(&_z1b); 477 | 478 | // the captured variables can instead 479 | // be moved to the closure 480 | let mut x2 = vec![1, 2, 3]; 481 | let y2 = move |value| { 482 | x2.push(value); 483 | }; 484 | // COMPILE ERROR: x2.push(5); 485 | 486 | // the type of such closures is FnMut 487 | // their usage is more restricted than Fn 488 | let _z2: &FnMut(i32) = &y2; 489 | 490 | // closures that move a value from 491 | // their environment can only be called once 492 | let x3 = vec![1, 2, 3]; 493 | let y3 = || { 494 | let _z3 = x3; 495 | }; 496 | y3(); 497 | // COMPILE ERROR: y3(); 498 | 499 | // the type of such closures is FnOnce 500 | // their usage is more restricted than FnMut 501 | } 502 | 503 | #[test] 504 | fn unit_test() { 505 | assert_eq!(1, 1); 506 | } 507 | 508 | /// Actually run by cargo test for library crates. 509 | /// 510 | /// assert_eq!(documentation(), "You bet"); 511 | pub fn documentation() -> String { 512 | "You bet".to_string() 513 | } 514 | 515 | fn main() { 516 | ownership(); 517 | references(); 518 | lifetimes(); 519 | lifetimes_structs(); 520 | mutability(); 521 | errors(); 522 | structs(); 523 | associated_functions(); 524 | enums(); 525 | traits(); 526 | closures(); 527 | } 528 | -------------------------------------------------------------------------------- /tests/integration_tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn integration_test() { 3 | // gets linked against a library's crate 4 | // used to test the public api 5 | assert_eq!(1, 1); 6 | } 7 | --------------------------------------------------------------------------------