commit 5944ccfce45b4b878e67b5b3db70514eb0a5d948 Author: Zykino Date: Tue Oct 16 21:06:19 2018 +0200 Initial commit: start => 6.1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53eaa21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..dca144e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "notes_from_rust_programing_language_second_edition" +version = "0.1.0" + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bc26509 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "notes_from_rust_programing_language_second_edition" +version = "0.1.0" +authors = ["Zykino "] + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..cfd45af --- /dev/null +++ b/src/main.rs @@ -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 --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 = Some(5); + let _z: Option = None; + + // let sum = _x + _y; // no implementation for `i8 + std::option::Option` + + 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!() +}