Initial commit: start => 6.1
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
/target
 | 
			
		||||
**/*.rs.bk
 | 
			
		||||
							
								
								
									
										4
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "notes_from_rust_programing_language_second_edition"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "notes_from_rust_programing_language_second_edition"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
authors = ["Zykino <Zykino@users.noreply.github.com>"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
							
								
								
									
										655
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										655
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,655 @@
 | 
			
		||||
fn main() {
 | 
			
		||||
    println!("Hello, world!");
 | 
			
		||||
    println!();
 | 
			
		||||
 | 
			
		||||
    chapter_1();
 | 
			
		||||
    chapter_3();
 | 
			
		||||
    chapter_2();
 | 
			
		||||
    chapter_4();
 | 
			
		||||
    chapter_5();
 | 
			
		||||
    chapter_6();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn chapter_1() {
 | 
			
		||||
    println!("Chapter 1: Getting Started");
 | 
			
		||||
    println!("
 | 
			
		||||
The compiler is named `rustc`, the package manager is `cargo`.
 | 
			
		||||
Using cargo provide a lot of ease in setting up and building a rust project.
 | 
			
		||||
    `cargo new <project_name> --bin` create a folder with a hello word program, a git repo ready. *Check the `Cargo.toml` file.*
 | 
			
		||||
    `cargo run` build (if necessary) and run the program.
 | 
			
		||||
    `cargo check` check that the build pass but don't create the executable (faster than `cargo build`).
 | 
			
		||||
    `cargo build --release` create the release executable (with optimizations) and store it under `./target/release`. *Use this build for benchmark.*
 | 
			
		||||
    `cargo help` to get some help.
 | 
			
		||||
    `cargo clean` to clean the crate.
 | 
			
		||||
    ");
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn chapter_3() {
 | 
			
		||||
    println!("Chapter 3: Common Programming Concepts");
 | 
			
		||||
    variable_and_mutability();
 | 
			
		||||
    data_types();
 | 
			
		||||
    println!("Return value of the Functions section: {}", functions(-2, 44));
 | 
			
		||||
    control_flow();
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn variable_and_mutability() {
 | 
			
		||||
    println!("Variable and mutability");
 | 
			
		||||
 | 
			
		||||
    println!("We cannot change a variable once it is set. By default they are immutable.");
 | 
			
		||||
    let x = 5;
 | 
			
		||||
    println!("The value of x is: {}", x);
 | 
			
		||||
    // x = 6;      // cannot assign twice to immutable variable
 | 
			
		||||
 | 
			
		||||
    println!("We can render the variables mutable by adding the `mut` keyword in the declaration: `let mut toto = 42;`.");
 | 
			
		||||
    let mut y = 5;
 | 
			
		||||
    println!("The value of y is: {}", y);
 | 
			
		||||
    y = 6;
 | 
			
		||||
    println!("The value of y is: {}", y);
 | 
			
		||||
 | 
			
		||||
    println!("The const should be resolve at compile time. The type must be explicited. They are best for naming hardcoded values.");
 | 
			
		||||
    const MAX_POINTS: u32 = 100000;
 | 
			
		||||
    const MAX_POINTS_EASY_READ: u32 = 100_000;
 | 
			
		||||
    println!("The value of `const MAX_POINTS` is: {}", MAX_POINTS);
 | 
			
		||||
    println!("The value of `const MAX_POINTS_EASY_READ` is: {} wrote with an underscore: 100_000 = 100000", MAX_POINTS_EASY_READ);
 | 
			
		||||
 | 
			
		||||
    println!("There is a concept of shadowing a variable to reuse the same name but change it's type. We can also keep the variable immutable while still changing it's value but I have yet to find how this can be usefull.");
 | 
			
		||||
    let z = 1;
 | 
			
		||||
    println!("The value of z (not mut) is: {}", z);
 | 
			
		||||
    let z = z + 4;
 | 
			
		||||
    println!("The value of z (not mut) is: {}", z);
 | 
			
		||||
    let z = z * 2;
 | 
			
		||||
    println!("The value of z (not mut) is: {}", z);
 | 
			
		||||
    let spaces = "     ";       // We asked to show the number of space char we need to use.
 | 
			
		||||
    println!("The value of spaces (not mut) is: `{}`", spaces);
 | 
			
		||||
    let spaces = spaces.len();  // But we prefer to store it as an int;
 | 
			
		||||
    println!("The value of spaces (not mut) is: `{}`", spaces);
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn data_types() {
 | 
			
		||||
    println!("Data types");
 | 
			
		||||
    println!("Data types: Scalar");
 | 
			
		||||
    println!("`let var_name = value;`");
 | 
			
		||||
    println!("`let var_name: type = value;`");
 | 
			
		||||
 | 
			
		||||
    println!("Data types: Scalar: Integer (default: i32)");
 | 
			
		||||
    println!("The differents int types are: [i8, i16, i32, i64, isize], change `i` to `u` for unsigned.");
 | 
			
		||||
    println!("A visual separator can be used with the `_` char: `10_000`.");
 | 
			
		||||
    println!("Notation Hex: 0xFF.");
 | 
			
		||||
    println!("Notation Octal: 0o77.");
 | 
			
		||||
    println!("Notation Binary: 0b1111_0000.");
 | 
			
		||||
    println!("Notation Byte (u8): b'A', b' '.");
 | 
			
		||||
    println!("It is possible to suffix the value with the type (except for the byte form): [42u8, 0x05u16, 0o42i32, 0b1111_0000u64]");
 | 
			
		||||
 | 
			
		||||
    println!("Data types: Scalar: Float (default: f64)");
 | 
			
		||||
    println!("f32 = float, f64 = double.");
 | 
			
		||||
    println!("Data types: Scalar: Operations");
 | 
			
		||||
    println!("[+, -, *, /, %]");
 | 
			
		||||
    println!("Data types: Scalar: Bool");
 | 
			
		||||
    println!("[true, false]");
 | 
			
		||||
    println!("Data types: Scalar: Char");
 | 
			
		||||
    println!("Limited by the a single quote: `'` (string use double quote `\"`). It represent a Unicode Scalar Value");
 | 
			
		||||
    let c = 'z';
 | 
			
		||||
    let z = 'ℤ';
 | 
			
		||||
    let heart_eyed_cat = '😻';
 | 
			
		||||
    println!("Some valid char values: `{}`, `{}`, `{}`", c, z, heart_eyed_cat);
 | 
			
		||||
    println!("See chapter 8 for strange thing that can happend because of the Unicode representation.");
 | 
			
		||||
 | 
			
		||||
    println!("Data types: Compound");
 | 
			
		||||
    println!("Data types: Compound: Tuple");
 | 
			
		||||
    println!("Some kind of not named object");
 | 
			
		||||
    let tuple: (i32, f64, u8) = (500, 6.4, 1);
 | 
			
		||||
    let (x, y, z) = tuple;
 | 
			
		||||
    println!("The value of (destructing) x: {}, y: {}, z: {}", x, y, z);
 | 
			
		||||
    let tup = (500, 6.4, 1);
 | 
			
		||||
    let x = tup.0;
 | 
			
		||||
    let y = tup.1;
 | 
			
		||||
    let z = tup.2;
 | 
			
		||||
    println!("The value of (tup.0) x: {}, y: {}, z: {}", x, y, z);
 | 
			
		||||
 | 
			
		||||
    println!("Data types: Compound: Array");
 | 
			
		||||
    println!("An array is fixed on size. For a changing size array see vector in chapter 8.");
 | 
			
		||||
    let a = [0, 1, 2, 3, 4];
 | 
			
		||||
    println!("The value of a[0]: {}, a[1]: {}", a[0], a[1]);
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn functions(x: i32, y: i32) -> i32 {
 | 
			
		||||
    println!("Functions");
 | 
			
		||||
    println!("The parameters must be typed.");
 | 
			
		||||
    println!("Parameters values are x: {}, y; {}", x, y);
 | 
			
		||||
 | 
			
		||||
    println!("Statement do not return a value, Expressions does.");
 | 
			
		||||
    println!("A block is an Expression. (There are also other Expressions.)");
 | 
			
		||||
    println!("We can transform an Expression into a Statement by closing it with a `;`.");
 | 
			
		||||
    let a = 5;
 | 
			
		||||
    let b = {
 | 
			
		||||
        let a = 3;
 | 
			
		||||
        a + 1
 | 
			
		||||
    };
 | 
			
		||||
    println!("The value of a: {}, b: {}", a, b);
 | 
			
		||||
 | 
			
		||||
    // To escape the curly braces in formated strings we need to format them with others around them: {{}} => {}
 | 
			
		||||
    println!("Return value type is declared after an arrow symbol: `fn function(param: i16) -> i16 {{}}`");
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
    // Equivalent to: `return x + y;` == `x + y`
 | 
			
		||||
    // NOTE: there is no semicolon at the end of the Expression (wich would have transform it into a Statement and produced a compile time error).
 | 
			
		||||
    x + y
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn control_flow() {
 | 
			
		||||
    println!("Control flow");
 | 
			
		||||
    println!("If expression");
 | 
			
		||||
    let number = 6;
 | 
			
		||||
    if number <= 5 {
 | 
			
		||||
        println!("Condition was true");
 | 
			
		||||
    } else {
 | 
			
		||||
        println!("Condition was false");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let divisor = if number % 4 == 0 {
 | 
			
		||||
        "Four"
 | 
			
		||||
    } else if number % 3 == 0{
 | 
			
		||||
        "Three"
 | 
			
		||||
    } else if number % 2 == 0 {
 | 
			
		||||
        "Two"
 | 
			
		||||
    } else {
 | 
			
		||||
        "Unknown"
 | 
			
		||||
    };
 | 
			
		||||
    println!("I found the divisor: {} for the number: {}", divisor, number);
 | 
			
		||||
 | 
			
		||||
    println!("Loops");
 | 
			
		||||
    let mut number = 6;
 | 
			
		||||
    loop {
 | 
			
		||||
        println!("`loop` are infinite unless stoped with a `break` instruction.");
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while number > 0 {
 | 
			
		||||
        println!("{}!", number);
 | 
			
		||||
        number -= 1;
 | 
			
		||||
    }
 | 
			
		||||
    println!("LIFTOFF!!!");
 | 
			
		||||
 | 
			
		||||
    let array = [10, 20, 30, 40, 50];
 | 
			
		||||
    for element in array.iter() {
 | 
			
		||||
        println!("The value is: {}", element);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for element in (1..5).rev() {
 | 
			
		||||
        println!("{}!", element);
 | 
			
		||||
    }
 | 
			
		||||
    println!("TO THE STARS AND BEYOND!!!");
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn chapter_2() {
 | 
			
		||||
    println!("Chapter 2: Guess game (see project guessing_game)");
 | 
			
		||||
    println!("To add a dependence add it's `name = version_str` in `Cargo.toml`, see crates.io. To update, run `cargo update`.");
 | 
			
		||||
    println!("`use std::io;` to include io from std, the use `io::stdin()...`. We can also not include it and directly call `std::io::stdin()...`");
 | 
			
		||||
    println!("To use a crate we also need to add it in the program with `external crate rand;` followed by `use rand::Rng` if we want.");
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn chapter_4() {
 | 
			
		||||
    println!("Chapter 4: Ownership");
 | 
			
		||||
    ownership();
 | 
			
		||||
    reference_and_borrowning();
 | 
			
		||||
    slices();
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn ownership() {
 | 
			
		||||
    println!("Ownership and Functions");
 | 
			
		||||
    {
 | 
			
		||||
        let string = String::from("hello"); // string comes into scope
 | 
			
		||||
 | 
			
		||||
        takes_ownership(string);            // string's value moves into the function
 | 
			
		||||
        // println!("{}", string);          // ... and so is no longer valid here
 | 
			
		||||
 | 
			
		||||
        let x = 5;                          // x comes into scope
 | 
			
		||||
 | 
			
		||||
        makes_copy(5);                      // x would move into the function, but i32 is Copy (trait)
 | 
			
		||||
        println!("{}", x);                  // so it's okay to still use x afterward
 | 
			
		||||
    }       // Here, x goes out of scope, then string. But because string's value was moved,
 | 
			
		||||
            // nothing special happens.
 | 
			
		||||
 | 
			
		||||
    println!("Return Values and Scope");
 | 
			
		||||
    {
 | 
			
		||||
        let s1 = gives_ownership();         // gives_ownership moves its return value into s1
 | 
			
		||||
        let s2 = String::from("hello");     // s2 comes into scope
 | 
			
		||||
        let s3 = takes_and_gives_back(s2);  // s2 is moved into takes_and_gives_back,
 | 
			
		||||
                                            // which also moves its return value into s3
 | 
			
		||||
 | 
			
		||||
        println!("s1: {}", s1);
 | 
			
		||||
        // println!("s2: {}", s2);
 | 
			
		||||
        println!("s3: {}", s3);
 | 
			
		||||
 | 
			
		||||
    }       // Here, s3 goes out of scope and is dropped.
 | 
			
		||||
            // s2 goes out of scope but was moved, so nothing happens.
 | 
			
		||||
            // s1 goes out of scope and is gropped.
 | 
			
		||||
 | 
			
		||||
    println!("Return Values and Scope");
 | 
			
		||||
    {
 | 
			
		||||
        let s1 = String::from("hello");
 | 
			
		||||
        let (s2, len) = calculate_length(s1);
 | 
			
		||||
 | 
			
		||||
        println!("REMINDER: we can return a tuple and destructure it immediately: `{}`'s lenth is: {} ", s2, len);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn takes_ownership(some_string: String) {   // some_string comes into scope
 | 
			
		||||
    println!("{}", some_string);
 | 
			
		||||
}       // Here, some_string goes out of scope and `drop` is called.
 | 
			
		||||
        // The backing memory is freed.
 | 
			
		||||
 | 
			
		||||
fn makes_copy(some_integer: i32) {          // some_integer comes into scope
 | 
			
		||||
    println!("{}", some_integer);
 | 
			
		||||
}       // Here, some_integer goes out of scope.
 | 
			
		||||
        // It is just poped out of the stack, nothing special happens.
 | 
			
		||||
 | 
			
		||||
fn gives_ownership() -> String {            // gives_ownership will move its return value
 | 
			
		||||
                                            // into the function that calls it
 | 
			
		||||
 | 
			
		||||
    let some_string = String::from("hello");// some_string comes into scope
 | 
			
		||||
    some_string                             // some_string is returned and moves out
 | 
			
		||||
                                            // to the calling function
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// takes_and_gives_back will take a String and return one (other)
 | 
			
		||||
fn takes_and_gives_back(a_string: String) -> String {   // a_string comes into scope
 | 
			
		||||
    a_string    // a_string is returned and moves to the calling function
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn calculate_length(s: String) -> (String, usize) {
 | 
			
		||||
    let length = s.len();   // len() returns the length of a String
 | 
			
		||||
    (s, length)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn reference_and_borrowning() {
 | 
			
		||||
    println!("Reference and Borrowing");
 | 
			
		||||
    let mut string = String::from("hello");
 | 
			
		||||
    let len = calculate_length_borrow(&string);
 | 
			
		||||
    println!("Now we sended the reference of the string (same as in C++) so we don't need a tuple: \t\t`{}`'s lenth is: {} ", string, len);
 | 
			
		||||
    let len = calculate_length_borrow_mut(&mut string);
 | 
			
		||||
    println!("Now we sended the mutable reference of the string (same as in C++) so we don't need a tuple: \t`{}`'s lenth is: {} ", string, len);
 | 
			
		||||
 | 
			
		||||
    println!("Dandling pointers are not possible.");
 | 
			
		||||
    // dandle();
 | 
			
		||||
    println!("We cannot returns a reference to a value created in the function but we can return the value (see lifetime in Chapter 10)");
 | 
			
		||||
    // gives_ownership();   // return the value created in the function instead of the reference.
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn calculate_length_borrow(s: &String) -> usize {   // s is a reference to a String
 | 
			
		||||
    s.len()
 | 
			
		||||
}       // Here, s goes out of scope. But nothing happens since it does not have ownership
 | 
			
		||||
        // of what it refers to.
 | 
			
		||||
 | 
			
		||||
fn calculate_length_borrow_mut(s: &mut String) -> usize {   // s is a reference to a mutable String
 | 
			
		||||
    s.push_str(", word");
 | 
			
		||||
    s.len()
 | 
			
		||||
}       // Here, s goes out of scope. But nothing happens since it does not have ownership
 | 
			
		||||
        // of what it refers to.
 | 
			
		||||
 | 
			
		||||
// fn dandle() -> &String {                    // dandle returns a reference to a String
 | 
			
		||||
//     let s = String:: from("hello");         // s is a new String
 | 
			
		||||
//     &s                                      // we return a reference to the String, s
 | 
			
		||||
// }       // Here, s goes out of scope, and is dropped. Its memory goes away
 | 
			
		||||
//         // DANGER !
 | 
			
		||||
 | 
			
		||||
#[allow(unused_mut)]
 | 
			
		||||
fn slices() {
 | 
			
		||||
    println!("Slice");
 | 
			
		||||
    println!("The range syntax is `..`. A slice is a reference to a range: `&s[0..5]`.");
 | 
			
		||||
    println!("On `start..end`, `start` is included while `end` is excluded: the range is `[0-5[`.");
 | 
			
		||||
    println!("If one side of the range correspond to the extremity of collection we can ommit it.`&s[0..s.len()] == &s[..]`");
 | 
			
		||||
 | 
			
		||||
    let s = String::from("hello world");
 | 
			
		||||
 | 
			
		||||
    let hello = &s[0..5];
 | 
			
		||||
    let world = &s[6..s.len()];
 | 
			
		||||
    let string = &s[0..s.len()];
 | 
			
		||||
 | 
			
		||||
    let hello_short = &s[..5];
 | 
			
		||||
    let world_short = &s[6..];
 | 
			
		||||
    let string_short = &s[..];
 | 
			
		||||
 | 
			
		||||
    assert!((hello == hello_short) == (hello == "hello"));
 | 
			
		||||
    assert!((world == world_short) == (world == "world"));
 | 
			
		||||
    assert!((string == string_short) == (string == "hello world"));
 | 
			
		||||
 | 
			
		||||
    println!("WARNING: slices in string do not work if the slice try to cut a char in half.");
 | 
			
		||||
    println!("Remember the char are UTF-8 and not ASCII so they use 1 or 2 Bytes. (Chapter 8)");
 | 
			
		||||
 | 
			
		||||
    let mut s = String::from("hello world");
 | 
			
		||||
    let hello = first_word(&s);
 | 
			
		||||
 | 
			
		||||
    //s.clear();   // cannot borrow `s` as mutable because it is also borrowed as immutable
 | 
			
		||||
 | 
			
		||||
    println!("The first word of `{}` is: {}", s, hello);
 | 
			
		||||
    println!("String literals are slices: `let s: &str = \"hello, world\"`, note that s is a reference str to a string literal.");
 | 
			
		||||
 | 
			
		||||
    println!("We can update our functions to have an API accepting `str` as well as `String`");
 | 
			
		||||
    let string = String::from("hello world");
 | 
			
		||||
    let string_literal = "hello world";
 | 
			
		||||
 | 
			
		||||
    // first_word_updated works on slices of `String`s
 | 
			
		||||
    let word_slice = first_word_updated(&string[..]);
 | 
			
		||||
    // first_word_updated works on slices of string literals
 | 
			
		||||
    let word_slice_literal = first_word_updated(&string_literal[..]);
 | 
			
		||||
    // Because string literals *are* slices already, this works too, witjout the slice syntax!
 | 
			
		||||
    let word_literal = first_word_updated(string_literal);
 | 
			
		||||
 | 
			
		||||
    // Also found out that `String`s can be dereferenced to `str`
 | 
			
		||||
    let word = first_word_updated(&string);
 | 
			
		||||
 | 
			
		||||
    println!("String slice: {}", word_slice);
 | 
			
		||||
    println!("str slice: {}", word_slice_literal);
 | 
			
		||||
    println!("str: {}", word_literal);
 | 
			
		||||
    println!("String dereferenced: {}", word);
 | 
			
		||||
 | 
			
		||||
    let a = [1, 2, 3, 4, 5];
 | 
			
		||||
    let slice = &a[1..3];
 | 
			
		||||
    println!("&a[1..3] slice of `{:?}` is: `{:?}`", a, slice);
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn first_word(s: &String) -> &str {
 | 
			
		||||
    // WARNING: Assumption that s is ASCII. See Chapter 8.
 | 
			
		||||
    let bytes = s.as_bytes();
 | 
			
		||||
 | 
			
		||||
    for (i, &e) in bytes.iter().enumerate() {
 | 
			
		||||
        if e == b' ' {
 | 
			
		||||
            return &s[0..i]
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Only one word, we return the entire slice
 | 
			
		||||
    &s[..]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn first_word_updated(s: &str) -> &str {
 | 
			
		||||
    // WARNING: Assumption that s is ASCII. See Chapter 8.
 | 
			
		||||
    let bytes = s.as_bytes();
 | 
			
		||||
 | 
			
		||||
    for (i, &e) in bytes.iter().enumerate() {
 | 
			
		||||
        if e == b' ' {
 | 
			
		||||
            return &s[0..i]
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Only one word, we return the entire slice
 | 
			
		||||
    &s[..]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn chapter_5() {
 | 
			
		||||
    println!("Chapter 5: Structs");
 | 
			
		||||
    structs();
 | 
			
		||||
    struct_examples();
 | 
			
		||||
    method_syntax();
 | 
			
		||||
 | 
			
		||||
    println!()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn structs() {
 | 
			
		||||
    println!("Structures syntax and utilisations.");
 | 
			
		||||
    struct User {
 | 
			
		||||
        username: String,
 | 
			
		||||
        email: String,
 | 
			
		||||
        sign_in_count: u64,
 | 
			
		||||
        active: bool,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let user1 = User {
 | 
			
		||||
        email: String::from("someone@example.com"),
 | 
			
		||||
        username: String::from("someusername123"),
 | 
			
		||||
        active: true,
 | 
			
		||||
        sign_in_count: 1,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    println!("We can access struct element with the dot notation.");
 | 
			
		||||
    println!("user1.username: {} user1.email: {} user1.active: {} user1.sign_in_count: {}", user1.username, user1.email, user1.active, user1.sign_in_count);
 | 
			
		||||
 | 
			
		||||
    let mut user2 = User {
 | 
			
		||||
        email: String::from("someone@example.com"),
 | 
			
		||||
        username: String::from("someusername123"),
 | 
			
		||||
        active: true,
 | 
			
		||||
        sign_in_count: 1,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    println!("To change a `key: value` pair we need to `mut` the whole `struct`: {}", user2.email);
 | 
			
		||||
    user2.email = String::from("anotheremail@example.com");
 | 
			
		||||
    println!("To change a `key: value` pair we need to `mut` the whole `struct`: {}", user2.email);
 | 
			
		||||
 | 
			
		||||
    let user3 = build_user(String::from("builderemail@example.com"), String::from("builder's name"));
 | 
			
		||||
    fn build_user(email: String, username: String) -> User {
 | 
			
		||||
        User {
 | 
			
		||||
            email,      // email: email,
 | 
			
		||||
            username,   // username: username,
 | 
			
		||||
            active: true,
 | 
			
		||||
            sign_in_count: 1,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    println!("We can build a struct from a builder function. There is a short syntax: {}", user3.username);
 | 
			
		||||
 | 
			
		||||
    let user4 = User {
 | 
			
		||||
        email: String::from("another@example.com"),
 | 
			
		||||
        username: String::from("anotherusername567"),
 | 
			
		||||
        ..user1
 | 
			
		||||
    };
 | 
			
		||||
    println!("We can create a struct from an previous one to have it's values as default. {}", user4.active);
 | 
			
		||||
 | 
			
		||||
    struct Color (i32, i32, i32);
 | 
			
		||||
    struct Point (i32, i32, i32);
 | 
			
		||||
 | 
			
		||||
    let black = Color(0, 0, 0);
 | 
			
		||||
    let _origin = Point(0, 0, 0);
 | 
			
		||||
 | 
			
		||||
    fn is_black(color: Color) -> bool {
 | 
			
		||||
        color.0 == 0 && color.1 == 0 && color.2 == 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    println!("We can also use Tuple Structs to name a tuple: {}.", is_black(black));
 | 
			
		||||
    println!("This let us give a meaning to tuples and lock a function to one type.");
 | 
			
		||||
    // is_black(_origin);       // mismatched types
 | 
			
		||||
 | 
			
		||||
    println!("There is also Unit-like structs without any fields which behabe similarly to `()` (See chapter 10)");
 | 
			
		||||
 | 
			
		||||
    println!()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn struct_examples() {
 | 
			
		||||
    println!("Example: from variables to tuples to structure");
 | 
			
		||||
    {
 | 
			
		||||
        let width = 30;
 | 
			
		||||
        let height = 50;
 | 
			
		||||
        println!("VARS: The area of the rectangle is {} square pixels.", area(width, height));
 | 
			
		||||
 | 
			
		||||
        fn area(width: u32, height: u32) -> u32 {
 | 
			
		||||
            width * height
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        let rect = (30, 50);
 | 
			
		||||
        println!("TUPLE: The area of the rectangle is {} square pixels.", area(rect));
 | 
			
		||||
 | 
			
		||||
        fn area(dimensions: (u32, u32)) -> u32 {
 | 
			
		||||
            dimensions.0 * dimensions.1
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        #[derive(Debug)]
 | 
			
		||||
        struct Rectangle {
 | 
			
		||||
            width: u32,
 | 
			
		||||
            height: u32,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let rect = Rectangle { width: 30, height: 50 };
 | 
			
		||||
        println!("STRUCT: The area of the rectangle is {} square pixels.", area(&rect));
 | 
			
		||||
        println!("rect is {:#?}", rect);
 | 
			
		||||
 | 
			
		||||
        fn area(rectangle: &Rectangle) -> u32 {
 | 
			
		||||
            rectangle.width * rectangle.height
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn method_syntax() {
 | 
			
		||||
    println!("Method syntax");
 | 
			
		||||
 | 
			
		||||
    #[derive(Debug)]
 | 
			
		||||
    struct Rectangle {
 | 
			
		||||
        width: u32,
 | 
			
		||||
        height: u32,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl Rectangle {
 | 
			
		||||
        fn area(&self) -> u32 {
 | 
			
		||||
            self.width * self.height
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        let rect = Rectangle { width: 30, height: 50 };
 | 
			
		||||
        println!("STRUCT + Method: The area of the rectangle is {} square pixels.", rect.area());
 | 
			
		||||
        println!("rect is {:?}", rect);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    println!("It's possible to have plutiples `impl` blocks. (See chapter 10 to have an usefull way to use it)");
 | 
			
		||||
    impl Rectangle {
 | 
			
		||||
        fn can_hold(&self, other: &Rectangle) -> bool {
 | 
			
		||||
            self.width > other.width && self.height > other.height
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        let rect1 = Rectangle { width: 30, height: 50 };
 | 
			
		||||
        let rect2 = Rectangle { width: 10, height: 40 };
 | 
			
		||||
        let rect3 = Rectangle { width: 60, height: 45 };
 | 
			
		||||
 | 
			
		||||
        println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
 | 
			
		||||
        println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    println!("We can add a function inside an `impl` block instead of a method by not using the `self` parameter. Nice use for scoped constructors.");
 | 
			
		||||
    impl Rectangle {
 | 
			
		||||
        fn square(size: u32) -> Rectangle {
 | 
			
		||||
            Rectangle { width: size, height: size }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        let sq = Rectangle::square(3);
 | 
			
		||||
        println!("We createted a square with an associated function {:?}", sq);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    println!();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn chapter_6() {
 | 
			
		||||
    defining_enum();
 | 
			
		||||
    match_control_flow();
 | 
			
		||||
    concise_control_flow_if_let();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
fn defining_enum() {
 | 
			
		||||
    {
 | 
			
		||||
        #[derive(Debug)]
 | 
			
		||||
        enum IpAddrKind {
 | 
			
		||||
            V4,
 | 
			
		||||
            V6,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let ip_v4 = IpAddrKind::V4;
 | 
			
		||||
        let ip_v6 = IpAddrKind::V6;
 | 
			
		||||
 | 
			
		||||
        print_ip(ip_v4);
 | 
			
		||||
        print_ip(ip_v6);
 | 
			
		||||
 | 
			
		||||
        fn print_ip(ip_type: IpAddrKind) {
 | 
			
		||||
            println!("The ip type is: {:?}", ip_type);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        struct IpAddr {
 | 
			
		||||
            kind: IpAddrKind,
 | 
			
		||||
            address: String,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let _home = IpAddr {
 | 
			
		||||
            kind: IpAddrKind::V4,
 | 
			
		||||
            address: String::from("127.0.0.1"),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let _loopback = IpAddr {
 | 
			
		||||
            kind: IpAddrKind::V6,
 | 
			
		||||
            address: String::from("127.0.0.1"),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        #[derive(Debug)]
 | 
			
		||||
        enum IpAddr {
 | 
			
		||||
            V4(u8, u8, u8, u8),
 | 
			
		||||
            V6(String),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let home = IpAddr::V4(127, 0, 0, 1);
 | 
			
		||||
        let loopback = IpAddr::V6(String::from("::1"));
 | 
			
		||||
 | 
			
		||||
        println!("Instead of combining an enum and a struct, we can put the datas directly into the enum: {:?}", home);
 | 
			
		||||
        println!("Doing so also permit to have differents types for each elements: {:?}", loopback);
 | 
			
		||||
        println!("NOTE: The standard library implements an IpAddr enum, beter use it than our custom one 😉.");
 | 
			
		||||
 | 
			
		||||
        impl IpAddr {
 | 
			
		||||
            fn route(&self)  {
 | 
			
		||||
                // Method would be defined here
 | 
			
		||||
                unimplemented!()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        println!("Also, like the `struct`, we can implement define mehods and functions to the `enum`");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        println!("The `Option` enum and its advantages over null values");
 | 
			
		||||
        println!("In rust there is no `null` values like in C/C++/... Instead there is a commonly use enum: the `Option`.");
 | 
			
		||||
 | 
			
		||||
        let _x: i8 = 5;
 | 
			
		||||
        let _y: Option<i8> = Some(5);
 | 
			
		||||
        let _z: Option<i8> = None;
 | 
			
		||||
 | 
			
		||||
        // let sum = _x + _y;   // no implementation for `i8 + std::option::Option<i8>`
 | 
			
		||||
 | 
			
		||||
        println!("Since _x: {:?} and _y: {:?} are not of the same type, the compiler will not let us compute `int + Option.`", _x, _y);
 | 
			
		||||
        println!("We will have to take care of the `Option` enum, so checking if the value is _y: {:?} `Some` or _z: {:?} `None`.", _y, _z);
 | 
			
		||||
        println!("Instead of believing there is a value when really there is a null value.");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn match_control_flow() {
 | 
			
		||||
    unimplemented!()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn concise_control_flow_if_let() {
 | 
			
		||||
    unimplemented!()
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user