├── README.md ├── main.rs └── mod.rs /README.md: -------------------------------------------------------------------------------- 1 | # Rust-Tutorial 2 | Rust is the language of choice for those looking for high performance, memory safety and all the tools needed to write error free code with ease. In this tutorial I created a full course on programming with Rust. 3 | -------------------------------------------------------------------------------- /main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Rust provides high performance similar to C++ and is a systems 3 | programming language that is excellent at reducing memory 4 | related errors. (Systems Programming == Low Level Details 5 | like Hardware and Memory) 6 | */ 7 | 8 | /* 9 | Garbage collection is not necessary. Memory errors often lead to 10 | security breaches which makes Rust very attractive. Rust programs 11 | also tend to require much less memory in comparison to other 12 | languages. 13 | */ 14 | 15 | /* 16 | Also Rust excels when it comes to concurrent programming. At compile 17 | time many possible concurrent programming problems are pointed out. 18 | Rusts compiler is so robust that it normally finds errors not found 19 | by other languages compilers. On top of that, these error messages 20 | are normally very easy to understand! 21 | */ 22 | 23 | /* 24 | Cargo is the Rust package manager and build system. It downloads 25 | libraries and builds them. You can verify it is installed with cargo --version 26 | Installation Details are at the end of the video 27 | Create Project with Cargo : 28 | cargo new rust_tutorial 29 | cd rust_tutorial 30 | */ 31 | 32 | /* 33 | This generates a Git repository, src directory and a TOML file 34 | TOML (Tom's Obvious Minimal Language) is the Cargo config file 35 | Contains info on configuring the package along with Dependencies 36 | Cargo.lock stores versions for all dependencies 37 | (cargo update updates all dependencies to latest versions) 38 | */ 39 | 40 | /* 41 | If you want to compile on any OS open your terminal, go to the directory 42 | cd /D D:\Tutorials\Rust Tutorial 43 | rustc main.rs 44 | ./main (or .\main.exe on Windows) 45 | */ 46 | 47 | /* 48 | To compile using Cargo (cargo run) or 49 | cargo build 50 | cd D:\Tutorials\Rust Tutorial\rust_tut\target\debug 51 | rust_tut.exe 52 | Cargo is best to use when you have multiple Crates (Packages of Code) 53 | */ 54 | 55 | // You can check if the code compiles without building 56 | // cargo check 57 | 58 | // When your code is ready for release compile with optimizations 59 | // cargo build --release 60 | 61 | // You'll get warnings if you have unused variables 62 | // This gets rid of them 63 | #![allow(unused)] 64 | 65 | // ----- LIBRARIES ----- 66 | 67 | // Define that you want to use the input / output library 68 | use std::io; 69 | 70 | // You could bring all public items under io into scope 71 | // use std::io::*; 72 | 73 | // Generate random numbers (Add rand crate to Cargo.toml) 74 | use rand::Rng; 75 | 76 | // Used for working with files 77 | // You could also use nested paths like this 78 | use std::io::{Write, BufReader, BufRead, ErrorKind}; 79 | use std::fs::File; 80 | 81 | // Ordering compares values 82 | use std::cmp::Ordering; 83 | 84 | // main is where execution begins for your program 85 | // fn : Declares a new function 86 | // () : Is were the parameters would go but there are none 87 | // {} : Surrounds the code in the function 88 | 89 | // Declare that we want to use the restaurant module here 90 | mod restaurant; 91 | 92 | // Declare a specific function we'll use to access the 93 | // pizza_order module 94 | use crate::restaurant::order_food; 95 | 96 | // ----- FUNCTIONS ----- 97 | // You can define functions before or after main 98 | fn say_hello(){ 99 | println!("Hello"); 100 | } 101 | 102 | // You can pass arguments to functions 103 | fn get_sum(x: i32, y: i32){ 104 | println!("{} + {} = {}", x, y, x+y); 105 | } 106 | 107 | // Return a value 108 | fn get_sum_2(x: i32, y: i32) -> i32 { 109 | // This expression is returned 110 | // If you used a semicolon you'd get an error because 111 | // a statement don't evaluate to a value 112 | x + y 113 | } 114 | 115 | // You can also use return 116 | fn get_sum_3(x: i32, y: i32) -> i32 { 117 | return x + y; 118 | } 119 | 120 | // Return multiple values 121 | fn get_2(x: i32) -> (i32, i32){ 122 | return (x+1, x+2); 123 | } 124 | 125 | fn print_str(x: String){ 126 | println!("A string {}", x); 127 | } 128 | 129 | fn print_return_str(x: String) -> String{ 130 | println!("A string {}", x); 131 | x 132 | } 133 | 134 | fn change_string(name: &mut String){ 135 | name.push_str(" is Happy"); 136 | println!("Message : {}", name); 137 | } 138 | 139 | // This function sums values in a list (Receives reference to list) 140 | fn sum_list(list: &[i32]) -> i32 { 141 | let mut sum = 0; 142 | for &val in list.iter(){ 143 | sum += &val; 144 | } 145 | sum 146 | } 147 | 148 | // ----- GENERIC FUNCTION ----- 149 | // When defining a function that uses a generic place the name 150 | // inside angled brackets after the function name 151 | 152 | // The add trait specifies the functionality of + for different 153 | // types 154 | use std::ops::Add; 155 | 156 | // We get 2 generic types of the same type and return that same type 157 | fn get_sum_gen>(x: T, y: T) -> T { 158 | return x + y; 159 | } 160 | 161 | fn main() { 162 | // It is common to indent with 4 spaces 163 | // You can tell println is a macro because of the ! 164 | // and not a function 165 | println!("What is your name?"); 166 | 167 | // Define an mutable variable (Value can changed) 168 | // String::new : A function that returns an empty string 169 | let mut name = String::new(); 170 | 171 | /* 172 | By default variables are immutable (Value can't Change) 173 | but it is possible to use mutable variables 174 | It is beneficial to use immutable variables because then 175 | you don't have to track down how values change 176 | */ 177 | 178 | // This string is immutable 179 | // Define it is a string with double quotes 180 | let greeting = "Nice to meet you"; 181 | 182 | /* 183 | Receive input from the user with read_line 184 | name is passed as an argument to read_line 185 | & defines that this variable is a reference to the variable 186 | which allows read_line to save values directly to name 187 | You use mut to define that name is a mutable variable 188 | */ 189 | 190 | /* 191 | read_line returns io::Result which is an enum 192 | Enums have a fixed number of specific values (Ok or Err) 193 | If Err is returned the operation failed and Err can tell you why 194 | Result has an expect function that returns any error that occurred 195 | (We should handle this error, but we'll cover that later) 196 | */ 197 | io::stdin().read_line(&mut name) 198 | .expect("Didn't Receive Input"); 199 | 200 | // Were you have {} your variable values will be placed 201 | // To remove the newline after name use trim_end 202 | println!("Hello {}! {}", name.trim_end(), greeting); 203 | 204 | // ----- VARIABLES ----- 205 | 206 | // Constants can be declared in any scope and used globally 207 | // They differ from immutable variables in that their value 208 | // can't be defined at runtime (based on a function call for example) 209 | const ONE_MIL: u32 = 1_000_000; 210 | const PI: f32 = 3.141592; 211 | 212 | // You can define variables with the same name but with different 213 | // data types (Shadowing) 214 | let age = "47"; 215 | 216 | // Trim eliminates white space and parse converts the string into an int 217 | // We expect age to have an integer value and expect will throw an 218 | // error if this isn't true (We'll get more into error handling later) 219 | let mut age: u32 = age.trim().parse() 220 | .expect("Age wasn't assigned a number"); 221 | age = age + 1; 222 | 223 | println!("I'm {} and I want ${}", age, ONE_MIL); 224 | 225 | // ----- DATA TYPES ----- 226 | // Rust is statically typed which means all types must be defined 227 | // These types are autogenerated by the compiler or defined explicitly 228 | 229 | // Unsigned integer : u8, u16, u32, u64, u128, usize 230 | // Signed integer : i8, i16, i32, i64, i128, isize 231 | let max_u32 = u32::MAX; 232 | println!("Max u32 : {}", max_u32); 233 | println!("Max u64 : {}", u64::MAX); 234 | // usize depends on your computer (If 64 bit then it's 64 bit) 235 | println!("Max usize : {}", usize::MAX); 236 | println!("Max u128 : {}", u128::MAX); 237 | 238 | // Floating Points : f32, f64 239 | println!("Max f32 : {}", f32::MAX); 240 | println!("Max f64 : {}", f64::MAX); 241 | 242 | // Booleans can have either the value true or false 243 | let _is_true = true; 244 | 245 | // Characters are defined with single quotes 246 | // They can store most any type of character from any language 247 | let _my_grade = 'A'; 248 | 249 | // ----- MATH ----- 250 | 251 | // f32 has 6 digits of precision 252 | let num_1: f32 = 1.111111111111111; 253 | println!("f32 : {}", num_1 + 0.111111111111111); 254 | 255 | // f64 has 14 digits of precision 256 | let num_2: f64 = 1.111111111111111; 257 | println!("f64 : {}", num_2 + 0.111111111111111); 258 | 259 | // Basic math operators 260 | let num_3: u32 = 5; 261 | let num_4: u32 = 4; 262 | println!("5 + 4 = {}", num_3 + num_4); 263 | println!("5 - 4 = {}", num_3 - num_4); 264 | println!("5 * 4 = {}", num_3 * num_4); 265 | println!("5 / 4 = {}", num_3 / num_4); 266 | println!("5 % 4 = {}", num_3 % num_4); // Remainder 267 | 268 | // You can use var+= 1 instead of var = var + 1 269 | 270 | // Generate random values between 1 and 100 271 | let random_num = rand::thread_rng().gen_range(1..101); 272 | println!("Random : {}", random_num); 273 | 274 | // ----- IF EXPRESSIONS ----- 275 | let age = 8; 276 | 277 | if (age >= 1) && (age <= 18){ 278 | println!("Important Birthday"); 279 | } else if (age == 21) || (age == 50){ 280 | println!("Important Birthday"); 281 | } else if age >= 65 { 282 | println!("Important Birthday"); 283 | } else { 284 | println!("Not an Important Birthday"); 285 | } 286 | 287 | // ----- TERNARY OPERATOR ----- 288 | let mut my_age = 47; 289 | let can_vote = if my_age >= 18 { 290 | true 291 | } else { 292 | false 293 | }; 294 | println!("Can Vote : {}", can_vote); 295 | 296 | // ----- MATCH ----- 297 | // Match runs different code depending on conditions 298 | // The pattern and the code to be executed is called an arm 299 | // A match must match all possible values! 300 | 301 | // You can do what we did with if 302 | let age2 = 8; 303 | match age2 { 304 | 1..=18 => println!("Important Birthday"), // 1 through 18 305 | 21 | 50 => println!("Important Birthday"), // 21 or 50 306 | 65..=i32::MAX => println!("Important Birthday"), // > 65 307 | _ => println!("Not an Important Birthday"), // Default 308 | }; 309 | 310 | // Compares age to valid age and cmp returns an Ordering which 311 | // has either the value Less, Greater, or Equal 312 | my_age = 18; 313 | let voting_age = 18; 314 | match my_age.cmp(&voting_age) { 315 | Ordering::Less => println!("Can't Vote"), 316 | Ordering::Greater => println!("Can Vote"), 317 | Ordering::Equal => println!("You just gained the right to vote!"), 318 | }; 319 | 320 | // ----- ARRAYS ----- 321 | // Elements in an array must be of the same type 322 | // and have a fixed size 323 | let arr_1 = [1,2,3,4]; 324 | 325 | // Get value by index starting at 0 326 | println!("1st : {}", arr_1[0]); 327 | 328 | // Get array length 329 | println!("Length : {}", arr_1.len()); 330 | 331 | // ----- LOOP ----- 332 | // Create an infinite loop that ends when break is called 333 | let arr_2 = [1,2,3,4,5,6,7,8,9]; 334 | let mut loop_idx = 0; 335 | loop { 336 | if arr_2[loop_idx] % 2 == 0 { 337 | loop_idx += 1; 338 | continue; // Goes to beginning of loop 339 | } 340 | 341 | if arr_2[loop_idx] == 9 { 342 | break; // Breaks out of loop 343 | } 344 | 345 | println!("Val : {}", arr_2[loop_idx]); 346 | loop_idx += 1; 347 | } 348 | 349 | // ----- WHILE LOOP ----- 350 | // Looping based on a condition 351 | loop_idx = 0; 352 | while loop_idx < arr_2.len(){ 353 | println!("Arr : {}", arr_2[loop_idx]); 354 | loop_idx += 1; 355 | } 356 | 357 | // ----- FOR LOOP ----- 358 | // For works better for cycling through collections 359 | for val in arr_2.iter() { 360 | println!("Val : {}", val); 361 | } 362 | // Start Here 363 | // ----- TUPLES ----- 364 | // Tuples can contain multiple data types in a list of fixed size 365 | // We convert to strings with to_string() 366 | let my_tuple: (u8, String, f64) = (47, "Derek".to_string(), 50_000.00); 367 | 368 | // You can get values by index starting at 0 369 | println!("Name : {}", my_tuple.1); 370 | 371 | // You can assign values to multiple variables 372 | let (v1, _v2, _v3) = my_tuple; 373 | println!("Age : {}", v1); 374 | 375 | // ----- STRINGS ----- 376 | // There are 2 types of strings 377 | // 1. String : Vector of bytes that can be changed 378 | // 2. &str : Points to the string and allows for viewing 379 | 380 | // Create an empty growable string 381 | let mut st1 = String::new(); 382 | 383 | // Insert a character at the end of a string 384 | st1.push('A'); 385 | 386 | // Insert a string at the end 387 | st1.push_str(" word"); 388 | 389 | // Iterate through words by splitting at whitespace 390 | for word in st1.split_whitespace() { 391 | println!("{}", word); 392 | } 393 | 394 | // Replace a string (Use "" for deleting) 395 | let st2 = st1.replace("A", "Another"); 396 | println!("{}", st2); 397 | 398 | // Create string of characters 399 | let st3 = String::from("x r t b h k k a m c"); 400 | 401 | // Convert to a vector 402 | let mut v1: Vec = st3.chars().collect(); 403 | 404 | // Sort characters 405 | v1.sort(); 406 | 407 | // Remove duplicates 408 | v1.dedup(); 409 | 410 | // Cycle through vector 411 | for char in v1 { 412 | println!("{}", char); 413 | } 414 | 415 | // Create a string literal 416 | let st4: &str = "Random string"; 417 | 418 | // Convert to heap allocated String 419 | let mut st5: String = st4.to_string(); 420 | println!("{}", st5); 421 | 422 | // Convert string into an array of bytes 423 | let _byte_arr1 = st5.as_bytes(); 424 | 425 | // Get a slice of a string from index 0 to 5 426 | let st6 = &st5[0..6]; 427 | println!("{}", st6); 428 | 429 | // Get length of string 430 | println!("String Length : {}", st6.len()); 431 | 432 | // Delete values in a string if mutable 433 | st5.clear(); 434 | 435 | // Combine strings 436 | let st6 = String::from("Just some"); 437 | let st7 = String::from("words"); 438 | 439 | // st6 can no longer be used 440 | // You can only add a reference to a string to another 441 | let st8 = st6 + &st7; 442 | 443 | // Cycle through letters in a string and print unicode 444 | for char in st8.bytes() { 445 | println!("{}", char); 446 | } 447 | 448 | // Cycle through letters in a string and print characters 449 | for char in st8.chars() { 450 | println!("{}", char); 451 | } 452 | 453 | // ----- CASTING WITH AS ----- 454 | // You can convert to different types in multiple ways 455 | let int_u8: u8 = 5; 456 | let int2_u8: u8 = 4; 457 | // Cast using as 458 | let int3_u32: u32 = (int_u8 as u32) + (int2_u8 as u32); 459 | 460 | // ----- ENUMS ----- 461 | // Enums allow for the definition of custom data types 462 | 463 | // Create an Enum for days of week 464 | enum Day { 465 | Monday, 466 | Tuesday, 467 | Wednesday, 468 | Thursday, 469 | Friday, 470 | Saturday, 471 | Sunday 472 | } 473 | 474 | // Define function for Day enum 475 | impl Day { 476 | fn is_weekend(&self) -> bool { 477 | match self { 478 | Day::Saturday | Day::Sunday => true, 479 | _ => false 480 | } 481 | } 482 | } 483 | 484 | // Use enum to store todays day 485 | let today:Day = Day::Monday; 486 | 487 | // Perform different actions based on day 488 | match today { 489 | Day::Monday => println!("Everyone hates Monday"), 490 | Day::Tuesday => println!("Donut day"), 491 | Day::Wednesday => println!("Hump day"), 492 | Day::Thursday => println!("Pay day"), 493 | Day::Friday => println!("Almost Weekend"), 494 | Day::Saturday => println!("Weekend!!!"), 495 | Day::Sunday => println!("Weekend!!!"), 496 | } 497 | 498 | // Check if today is a weekend 499 | println!("Is today the weekend {}", today.is_weekend()); 500 | 501 | // ----- VECTORS ----- 502 | // Vectors are like arrays that can grow if mutable 503 | // They only store values of the same type 504 | 505 | // Create an empty vector with i32 506 | let _vec1: Vec = Vec::new(); 507 | 508 | // Create a vector with defined values 509 | let mut vec2 = vec![1, 2, 3, 4]; 510 | 511 | // Add values to the end of a vector 512 | vec2.push(5); 513 | 514 | // Get value by index 515 | println!("1st : {}", vec2[0]); 516 | 517 | // Verify value exists 518 | let _second: &i32 = &vec2[1]; 519 | match vec2.get(1) { 520 | Some(second) => println!("2nd : {}", second), 521 | None => println!("No 2nd value"), 522 | }; 523 | 524 | // Cycle and change values 525 | for i in &mut vec2 { 526 | *i *= 2; 527 | } 528 | 529 | // Cycle through vector values 530 | for i in &vec2 { 531 | println!("{}", i); 532 | } 533 | 534 | // Get number of values in a vector 535 | println!("Vec Length : {}", vec2.len()); 536 | 537 | // Remove and return the last value 538 | println!("Pop {:?}", vec2.pop()); 539 | 540 | // START HERE 541 | 542 | // ----- FUNCTIONS ----- 543 | say_hello(); 544 | get_sum(4, 5); 545 | println!("{} + {} = {}", 5, 3, get_sum_2(5, 3)); 546 | println!("{} + {} = {}", 7, 8, get_sum_3(7, 8)); 547 | 548 | // Get multiple values 549 | let (val_1, val_2) = get_2(3); 550 | println!("Nums : {} {}", val_1, val_2); 551 | 552 | let num_list = vec![1,2,3,4,5]; 553 | println!("Sum of list = {}", sum_list(&num_list)); 554 | 555 | // ----- GENERIC TYPES ----- 556 | // We can specify the data type to be used at a later time with generics 557 | // It is mainly used when we want to create functions that can work with 558 | // multiple data types. It is used with structs, enums, traits, etc. 559 | // which we'll talk about later 560 | println!("5 + 4 = {}", get_sum_gen(5,4)); 561 | println!("5.2 + 4.6 = {}", get_sum_gen(5.2,4.6)); 562 | 563 | // ----- OWNERSHIP ----- 564 | // Memory is managed through a system of ownership with 565 | // rules that are checked at compile time. 566 | // To understand this you must understand the Stack & Heap 567 | // Both are parts of memory 568 | 569 | // Stack : Stores values in a last in first out format 570 | // Data on the stack must have a defined fixed size 571 | 572 | // Heap : When putting data on the heap you request a certain 573 | // amount of space. The OS finds space available and returns 574 | // an address for that space called a pointer. 575 | 576 | // RULES 577 | // 1. Each value has a variable that's called its owner 578 | // 2. There is only one owner at a time 579 | // 3. When the owner goes out of scope the value disappears 580 | 581 | // While automatic deallocation of resources is great problems 582 | // can occur. Imagine you copied a string. If you do the string 583 | // just stores a pointer to the 1st index, the memory required 584 | // for each character and the number of characters. What happens if 585 | // we delete one of those strings? That information is deallocated 586 | // for both strings. That causes a problem called a double free error. 587 | // Because of that once you copy a string you can no longer access 588 | // the original as you see here : 589 | // let str1 = String::from("World"); 590 | // let srt2 = str1; 591 | // println!("Hello {}", str1); 592 | 593 | // If you want 2 copies use clone 594 | let str1 = String::from("World"); 595 | let _str2 = str1.clone(); 596 | println!("Hello {}", str1); 597 | 598 | // The above doesn't apply with data types : 599 | // Integers, bool, char, floats, tuples with the above data types only 600 | 601 | // Here the string was borrowed by the function 602 | let str3: String = String::from("World"); 603 | print_str(str3); 604 | 605 | // This throws an error because the string was dropped when the 606 | // function ends 607 | // println!("str3 = {}", str3); 608 | 609 | // You can avoid this by passing a reference to a variable without 610 | // transferring ownership (You could also return the variable from 611 | // the function) (Passing by reference is called Borrowing) 612 | let str4: String = String::from("World"); 613 | let str5 = print_return_str(str4); 614 | println!("str5 = {}", str5); 615 | 616 | // If a function borrows a reference it can't change it unless we 617 | // create a mutable version of it (You can only create one mutable 618 | // version in the function) 619 | let mut str6: String = String::from("Derek"); 620 | change_string(&mut str6); 621 | 622 | // ----- HASH MAPS ----- 623 | // Hash maps are used to store key / value pairs 624 | use std::collections::HashMap; 625 | 626 | // Create an empty hash map 627 | let mut heroes = HashMap::new(); 628 | 629 | // Insert in hashmap (To overwrite use the same key) 630 | heroes.insert("Superman", "Clark Kent"); 631 | heroes.insert("Batman", "Bruce Wayne"); 632 | heroes.insert("The Flash", "Barry Allen"); 633 | 634 | // Iterate over hashmap 635 | for(k, v) in heroes.iter(){ 636 | println!("{} = {}", k, v); 637 | } 638 | 639 | // Length of hashmap 640 | println!("Length : {}", heroes.len()); 641 | 642 | // Check for key in hashmap 643 | if heroes.contains_key(&"Batman"){ 644 | // Get value with key 645 | let the_batman = heroes.get(&"Batman"); 646 | match the_batman { 647 | Some(_x) => println!("Batman is a hero"), 648 | None => println!("Batman is not a hero"), 649 | } 650 | } 651 | 652 | // ----- STRUCTS ----- 653 | // A struct is a custom data type that stores multiple 654 | // types of data 655 | struct Customer{ 656 | name: String, 657 | address: String, 658 | balance: f32, 659 | } 660 | 661 | // Create struct 662 | let mut bob = Customer { 663 | name: String::from("Bob Smith"), 664 | address: String::from("555 Main St"), 665 | balance: 234.50 666 | }; 667 | 668 | // Change a value 669 | bob.address = String::from("505 Main St"); 670 | println!("Address : {}", bob.address); 671 | 672 | // You could accept multiple data types using generics like 673 | // we did with functions. If we had a rectangle struct 674 | // that could accept floats or ints we would need 2 generics 675 | /* struct Rectangle { 676 | length: T, 677 | height: U 678 | } 679 | The data type is defined when the struct is created 680 | let rec = Rectangle {length: 4, height: 10.5}; 681 | */ 682 | 683 | // We can tie struct properties to functions using Traits 684 | // You can create functions that can be used by any structs 685 | // that implement the right traits 686 | trait Shape { 687 | // This is a constructor which returns a Shape 688 | fn new(length: f32, width: f32) -> Self; 689 | 690 | // An area function that belongs to this trait 691 | fn area(&self) -> f32; 692 | } 693 | 694 | // Define rectangle and circle struct 695 | struct Rectangle {length: f32, width: f32} 696 | struct Circle {length: f32, width: f32} 697 | 698 | // Implement the trait for rectangle 699 | impl Shape for Rectangle{ 700 | // Constructor 701 | fn new(length: f32, width: f32) -> Rectangle { 702 | return Rectangle{length, width}; 703 | } 704 | 705 | // self allows us to refer to parameters for this struct 706 | fn area(&self) -> f32{ 707 | return self.length * self.width; 708 | } 709 | } 710 | 711 | // Implement the trait for circle 712 | impl Shape for Circle{ 713 | // Constructor 714 | fn new(length: f32, width: f32) -> Circle { 715 | return Circle{length, width}; 716 | } 717 | 718 | fn area(&self) -> f32{ 719 | return (self.length / 2.0).powf(2.0) * PI; 720 | } 721 | } 722 | 723 | // Create circle and rectangle with Shape 724 | let rec: Rectangle = Shape::new(10.0, 10.0); 725 | let circ: Circle = Shape::new(10.0, 10.0); 726 | 727 | println!("Rec Area : {}", rec.area()); 728 | println!("Circ Area : {}", circ.area()); 729 | 730 | // We can implement methods on structs using generics 731 | // implShape ... 732 | 733 | // ----- PACKAGES CRATES & MODULES ----- 734 | // It is very important to keep your code organized 735 | // You can split code into multiple files 736 | // Packages can contain multiple crates 737 | // You can define what code is public and which is private 738 | 739 | // Create a file called mod.rs in a directory named restaurant 740 | // in the src directory 741 | 742 | // Crates : Modules that produce a library or executable 743 | // Modules : Organize and handle privacy 744 | // Packages : Build, test and share crates 745 | // Paths : A way of naming an item such as a struct, function 746 | 747 | // Packages can contain 0 or 1 library crate and as many binary crates 748 | // as you want. If you want more binary crates create a folder 749 | // called bin (Create bin directory in src and create file in it 750 | // named more_stuff.rs) 751 | 752 | // Call for the public function that will allow us access to 753 | // the module 754 | order_food(); 755 | 756 | // ----- READING & WRITING TO FILES & ERROR HANDLING ----- 757 | // Rust doesn't have exceptions like other languages. It handles 758 | // recoverable errors with Result and the panic! macro for 759 | // unrecoverable errors 760 | 761 | // When the panic! macro executes your program prints an error 762 | // memory is cleaned up and the program quits 763 | // panic!("Terrible Error"); 764 | 765 | // Accessing an index that doesn't exist calls panic 766 | // let lil_arr = [1,2]; 767 | // println!("{}", lil_arr[10]); 768 | 769 | // File to create 770 | let path = "lines.txt"; 771 | 772 | // Result has 2 varients Ok and Err 773 | // enum Result { 774 | // Ok(T), 775 | // Err(E), } 776 | // Where T represents the data typeof the value returns and E 777 | // the type of error 778 | 779 | // Create file and handle errors with match 780 | let output = File::create(path); 781 | let mut output = match output { 782 | Ok(file) => file, 783 | Err(error) => { 784 | panic!("Problem creating file : {:?}", error); 785 | } 786 | }; 787 | 788 | // Write to file and define the panic! error message with expect 789 | write!(output, "Just some\nRandom Words").expect("Failed to write to file"); 790 | 791 | // Open the file and if everything is ok unwrap returns the file 792 | // and if not panic! triggers an error (You could replace unwrap with ?) 793 | // Read file using buffering 794 | let input = File::open(path).unwrap(); 795 | let buffered = BufReader::new(input); 796 | 797 | // Cycle through and print the lines 798 | for line in buffered.lines() { 799 | println!("{}", line.unwrap()); 800 | } 801 | 802 | // You can also catch specific errors 803 | // Here I'll try to open a file and trigger an error if the file 804 | // couldn't be created, or use a default 805 | let output2 = File::create("rand.txt"); 806 | let output2 = match output2 { 807 | Ok(file) => file, 808 | Err(error) => match error.kind() { 809 | ErrorKind::NotFound => match File::create("rand.txt") { 810 | Ok(fc) => fc, 811 | Err(e) => panic!("Can't create file: {:?}", e), 812 | }, 813 | _other_error => panic!("Problem opening file : {:?}", error), 814 | }, 815 | }; 816 | 817 | // ----- ITERATORS ----- 818 | // We covered iterators before. They help us cycle through values in 819 | // arrays, vectors, maps, etc. 820 | // An iterator cycles through values by borrowing, so the collection 821 | // is not moved (You can't change values) 822 | let mut arr_it = [1,2,3,4]; 823 | for val in arr_it.iter() { 824 | println!("{}", val); 825 | } 826 | 827 | // You can create an iterator 828 | let mut iter1 = arr_it.iter(); 829 | // And call for each value with next 830 | println!("1st : {:?}", iter1.next()); 831 | 832 | // You could consume the collection with 833 | // arr_it.into_iter() but you'll no longer be able to use the collection 834 | 835 | // ----- CLOSURES ----- 836 | // A closure is a function without a name and they are sometimes 837 | // stored in a variable (They can be used to pass a function into 838 | // another function) 839 | // let var_name = |parameters| -> return_type {BODY} 840 | 841 | // Create a closure that defines if someone can vote 842 | let can_vote = |age: i32| { 843 | age >= 18 844 | }; 845 | println!("Can vote : {}", can_vote(8)); 846 | 847 | // Closures can access variables outside of its body with borrowing 848 | let mut samp1 = 5; 849 | let print_var = || println!("samp1 = {}", samp1); 850 | print_var(); 851 | samp1 = 10; 852 | 853 | // You can change values if you mark the closure mutable 854 | let mut change_var = || samp1 += 1; 855 | change_var(); 856 | println!("samp1 = {}", samp1); 857 | samp1 = 10; 858 | println!("samp1 = {}", samp1); 859 | 860 | // You can pass closures to functions 861 | fn use_func(a: i32, b: i32, func: T) -> i32 where T: Fn(i32, i32) -> i32 { 862 | func(a, b) 863 | } 864 | 865 | let sum = |a, b| a + b; 866 | let prod = |a, b| a * b; 867 | 868 | println!("5 + 4 = {}", use_func(5, 4, sum)); 869 | println!("5 * 4 = {}", use_func(5, 4, prod)); 870 | 871 | // ----- SMART POINTERS ----- 872 | // A pointer is an address to a location in memory. We have been 873 | // using them when we used the reference operator(&) to borrow 874 | // a value. 875 | 876 | // Strings and vectors are smart pointers. They own 877 | // data and also have functions for manipulating that data. 878 | 879 | // Smart pointers provide functionality beyond referencing locations 880 | // in memory. They can be used to track who has ownership of data. 881 | // Ownership is very important with Rust. 882 | 883 | // ----- BOX ----- 884 | 885 | // The Box smart pointer stores data on the heap instead of the stack. 886 | // All values are stored on the stack by default 887 | 888 | // Stack : Stores values in a last in first out format 889 | // Data on the stack must have a defined fixed size 890 | 891 | // Heap : When putting data on the heap you request a certain 892 | // amount of space. The OS finds space available and returns 893 | // an address for that space called a pointer. 894 | 895 | // A Box is normally used when you have a large amount of data stored 896 | // on the heap and then you pass pointers to it on the stack. 897 | 898 | // Create a Box with value 10 899 | let b_int1 = Box::new(10); 900 | 901 | // Get the value 902 | println!("b_int1 = {}", b_int1); 903 | 904 | // If we try to create a Binary tree we get the error 905 | // the size for values of type `str` cannot be known at 906 | // compilation time within `TreeNode` 907 | 908 | // This is saying we can't include nodes in a node because 909 | // the size of node depends on the size of multiple nodes 910 | // which confuses the compiler 911 | // struct TreeNode { 912 | // pub left: TreeNode, 913 | // pub right: TreeNode, 914 | // pub key: T, 915 | // } 916 | 917 | // We have other problems in that Binary Trees eventually end 918 | // and Rust doesn't like Null values so we have to use Option 919 | 920 | // We can use a Box here because it has a pointer to data and 921 | // a fixed size 922 | 923 | struct TreeNode { 924 | pub left: Option>>, 925 | pub right: Option>>, 926 | pub key: T, 927 | } 928 | 929 | // Create functions for creating nodes and adding left & right 930 | impl TreeNode { 931 | pub fn new(key: T) -> Self { 932 | TreeNode { 933 | left: None, 934 | right: None, 935 | key, 936 | } 937 | } 938 | 939 | pub fn left(mut self, node: TreeNode) -> Self { 940 | self.left = Some(Box::new(node)); 941 | self 942 | } 943 | 944 | pub fn right(mut self, node: TreeNode) -> Self { 945 | self.right = Some(Box::new(node)); 946 | self 947 | } 948 | } 949 | 950 | // Create the root node with left and right 951 | let node1 = TreeNode::new(1) 952 | .left(TreeNode::new(2)) 953 | .right(TreeNode::new(3)); 954 | 955 | 956 | // Used to test original 957 | // let mut boss = TreeNode { 958 | // left: None, 959 | // right: None, 960 | // key: 50, 961 | // }; 962 | 963 | // ----- CONCURRENCY ----- 964 | // Concurrent programming envolves executing different blocks of code 965 | // independently, while parallel programming is when different 966 | // code executes at the same time. A thread handles scheduling 967 | // and execution of these blocks of code. 968 | 969 | // Common problems with parallel programming involve : 970 | // 1. Thread are accessing data in the wrong order 971 | // 2. Threads are blocked from executing because of confusion 972 | // over requirements to proceed with execution 973 | 974 | use std::thread; 975 | use std::time::Duration; 976 | 977 | // // Create a thread with spawn 978 | // thread::spawn(|| { 979 | // for i in 1..25 { 980 | // println!("Spawned thread : {}", i); 981 | // // Forces thread to sleep and allow another thread to execute 982 | // thread::sleep(Duration::from_millis(1)); 983 | // } 984 | // }); 985 | 986 | // // There are no guarantees on when the threads will execute and 987 | // // that they will complete execution 988 | // for i in 1..20 { 989 | // println!("Main thread : {}", i); 990 | // thread::sleep(Duration::from_millis(1)); 991 | // } 992 | 993 | // If we assign the return value for this thread to a variable 994 | // and then call join on it our program will wait for it to stop 995 | // executing 996 | let thread1 = thread::spawn(|| { 997 | for i in 1..25 { 998 | println!("Spawned thread : {}", i); 999 | // Forces thread to sleep and allow another thread to execute 1000 | thread::sleep(Duration::from_millis(1)); 1001 | } 1002 | }); 1003 | 1004 | // There are no guarantees on when the threads will execute and 1005 | // that they will complete execution 1006 | for i in 1..20 { 1007 | println!("Main thread : {}", i); 1008 | thread::sleep(Duration::from_millis(1)); 1009 | } 1010 | 1011 | // We call join here so that the main thread executes with thread1 1012 | // unwrap handles the option Result which is Ok or Err 1013 | thread1.join().unwrap(); 1014 | 1015 | // ----- BANK ACCOUNT EXAMPLE ----- 1016 | // We will create a bank account that multiple customers will try 1017 | // to withdraw money from 1018 | 1019 | // Bank struct just contains current balance 1020 | pub struct Bank { 1021 | balance: f32 1022 | } 1023 | 1024 | // Allows for withdrawing money 1025 | // Pass a mutable reference so bank can be used elsewhere 1026 | // fn withdraw(the_bank: &mut Bank, amt: f32) { 1027 | // the_bank.balance -= amt; 1028 | // } 1029 | 1030 | 1031 | // Create bank struct 1032 | // let mut bank = Bank{balance: 100.00}; 1033 | // withdraw(&mut bank, 5.00); 1034 | // println!("Balance : {}", bank.balance); 1035 | 1036 | // Create a customer thread that withdraws money 1037 | // THIS WON'T WORK 1038 | // fn customer(the_bank: &mut Bank){ 1039 | // withdraw(the_bank, 5.00) 1040 | // } 1041 | 1042 | // Can't do this closure may outlive the current function, 1043 | // but it borrows `bank`, which is owned by the current function 1044 | // If a thread can outlive the main function and the main function 1045 | // has the bank which causes problems 1046 | // thread::spawn(|| { 1047 | // customer(&mut bank) 1048 | // }).join().unwrap(); 1049 | 1050 | // The fix that allows multiple owners and blocks access when needed 1051 | // A smart pointer Rc> allows multiple owners with mutable 1052 | // access to the same data 1053 | use std::rc::Rc; 1054 | use std::cell::RefCell; 1055 | // Arc provides shared ownership of a value 1056 | // Mutex blocks threads waiting for lock to be available 1057 | use std::sync::{Arc, Mutex}; 1058 | 1059 | fn withdraw(the_bank: &Arc>, amt:f32){ 1060 | let mut bank_ref = the_bank.lock().unwrap(); 1061 | 1062 | if bank_ref.balance < 5.00{ 1063 | println!("Current Balance : {} Withdrawal a smaller amount", 1064 | bank_ref.balance); 1065 | } else { 1066 | bank_ref.balance -= amt; 1067 | println!("Customer withdrew {} Current Balance {}", 1068 | amt, bank_ref.balance); 1069 | } 1070 | } 1071 | 1072 | fn customer(the_bank: Arc>) { 1073 | withdraw(&the_bank, 5.00); 1074 | } 1075 | 1076 | let bank: Arc> = 1077 | Arc::new(Mutex::new(Bank { balance: 20.00 })); 1078 | 1079 | // Creates 10 customer threads 1080 | let handles = (0..10).map(|_| { 1081 | 1082 | // Clone duplicates an the bank object 1083 | let bank_ref = bank.clone(); 1084 | thread::spawn(|| { 1085 | customer(bank_ref) 1086 | }) 1087 | }); 1088 | 1089 | // Wait for all customers to finish 1090 | for handle in handles { 1091 | handle.join().unwrap(); 1092 | } 1093 | 1094 | println!("Total: {}", bank.lock().unwrap().balance); 1095 | 1096 | // ----- INSTALLATION ------ 1097 | // Install rustup on Mac or Linux 1098 | // curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh 1099 | 1100 | } 1101 | -------------------------------------------------------------------------------- /mod.rs: -------------------------------------------------------------------------------- 1 | // Creates a module 2 | // Contains other modules which hold functions, 3 | // structs, enums, constants, traits 4 | // You use modules to organize your code and to make 5 | // parts of it private (Everything is Private by Default) 6 | // Parent modules can't access private items in child modules 7 | // but children can always access parent items 8 | 9 | mod pizza_order { 10 | 11 | // To access the struct and the part to make public must both use pub 12 | pub struct Pizza { 13 | pub dough: String, 14 | pub cheese: String, 15 | pub topping: String, 16 | } 17 | 18 | // Implement functionality for the Pizza struct 19 | impl Pizza { 20 | pub fn lunch(topping: &str) -> Pizza { 21 | Pizza { 22 | dough: String::from("regular dough"), 23 | cheese: String::from("mozzarella"), 24 | topping: String::from(topping), 25 | } 26 | } 27 | } 28 | 29 | // help_customer is public so functions can call it 30 | pub mod help_customer { 31 | // This function is private 32 | fn seat_at_table() { 33 | println!("Customer seated at table"); 34 | } 35 | 36 | // Making help_customer public doesn't make this child 37 | // function public so we must also make it public 38 | pub fn take_order() { 39 | seat_at_table(); 40 | 41 | // super allows me to access pizza in the parent scope 42 | let cust_pizza: super::Pizza = 43 | super::Pizza::lunch("veggies"); 44 | 45 | serve_customer(cust_pizza); 46 | } 47 | 48 | fn serve_customer(cust_pizza: super::Pizza){ 49 | println!("The customer is served a regular pizza with {}", cust_pizza.topping); 50 | } 51 | 52 | } 53 | } 54 | 55 | // This is the public function that allows our other file access 56 | pub fn order_food() { 57 | crate::restaurant::pizza_order::help_customer::take_order(); 58 | } 59 | --------------------------------------------------------------------------------