Initial commit: start => 6.1
This commit is contained in:
commit
5944ccfce4
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!()
|
||||
}
|
Loading…
Reference in New Issue
Block a user