1041 lines
34 KiB
Rust
1041 lines
34 KiB
Rust
fn main() {
|
||
println!("Hello, world!");
|
||
println!();
|
||
|
||
chapter_1();
|
||
chapter_3();
|
||
chapter_2();
|
||
chapter_4();
|
||
chapter_5();
|
||
chapter_6();
|
||
chapter_7();
|
||
chapter_8();
|
||
chapter_9();
|
||
}
|
||
|
||
fn chapter_1() {
|
||
println!("Chapter 1: Getting Started");
|
||
println!("`rustup update` to update rust.");
|
||
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() {
|
||
println!("Chapter 6: Enums and pattern matching");
|
||
defining_enum();
|
||
match_control_flow();
|
||
concise_control_flow_if_let();
|
||
|
||
println!();
|
||
}
|
||
|
||
#[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, better 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.");
|
||
}
|
||
|
||
println!();
|
||
}
|
||
|
||
fn match_control_flow() {
|
||
{
|
||
#[derive(Debug)]
|
||
enum UsState {
|
||
_Alabama,
|
||
Alaska,
|
||
// ...
|
||
}
|
||
|
||
enum UsCoin {
|
||
Penny,
|
||
Nickel,
|
||
Dime,
|
||
Quarter(UsState),
|
||
}
|
||
|
||
fn value_in_cents(coin: UsCoin) -> u32 {
|
||
match coin {
|
||
UsCoin::Penny => {
|
||
println!("Lucky penny!");
|
||
1
|
||
}
|
||
UsCoin::Nickel => 5,
|
||
UsCoin::Dime => 10,
|
||
UsCoin::Quarter(state) => {
|
||
println!("State quarter from {:?}!", state);
|
||
25
|
||
}
|
||
}
|
||
}
|
||
|
||
println!("With the match control flow we can get the value of a Penny: {:?}", value_in_cents(UsCoin::Penny));
|
||
println!("Or any other US coin vakue of the `enum`. Nickel: {:?}", value_in_cents(UsCoin::Nickel));
|
||
println!("The compiler will make sure we mach every value possibles values when using the `match` control flow. Dime: {:?}", value_in_cents(UsCoin::Dime));
|
||
println!("It's like a `switch case break` where you have to make sure all options are covered.");
|
||
println!("We can even match with the value contains in the enum. Quarter: {:?}", value_in_cents(UsCoin::Quarter(UsState::Alaska)));
|
||
}
|
||
|
||
{
|
||
fn plus_one(x: Option<i32>) -> Option<i32> {
|
||
match x {
|
||
None => None,
|
||
Some(i) => Some(i + 1),
|
||
}
|
||
}
|
||
|
||
let five = Some(5);
|
||
let six = plus_one(five);
|
||
let none = plus_one(None);
|
||
|
||
println!("Example use of `match` with the `Option` enum: five = {:?}, plus_one = {:?}, None plus_one = {:?}", five, six, none);
|
||
|
||
let some_u8_value = 7u8;
|
||
match some_u8_value {
|
||
1 => println!("one"),
|
||
3 => println!("three"),
|
||
5 => println!("five"),
|
||
7 => println!("seven"),
|
||
_ => (), // `_` is a placeholder and `()` is the unit value
|
||
}
|
||
}
|
||
|
||
println!();
|
||
}
|
||
|
||
#[allow(dead_code, unused_assignments, unused_variables)]
|
||
fn concise_control_flow_if_let() {
|
||
println!("When we only want to match one element, a `match` control flow may be berbose.");
|
||
println!("Instead we can use the `if let [else]` syntax.");
|
||
|
||
let some_u8_value = Some(0u8);
|
||
|
||
// This is the same as ...
|
||
match some_u8_value {
|
||
Some(3) => println!("three"),
|
||
_ => (),
|
||
}
|
||
// ... this.
|
||
if let Some(3) = some_u8_value {
|
||
println!("three");
|
||
}
|
||
|
||
#[derive(Clone, Copy, Debug)]
|
||
enum UsState {
|
||
Alabama,
|
||
Alaska,
|
||
// ...
|
||
}
|
||
#[derive(Clone, Copy)]
|
||
enum UsCoin {
|
||
Penny,
|
||
Nickel,
|
||
Dime,
|
||
Quarter(UsState),
|
||
}
|
||
|
||
let coin = UsCoin::Penny;
|
||
|
||
// And this is the same as ...
|
||
let mut count = 0;
|
||
match coin {
|
||
UsCoin::Quarter(state) => println!("State quarter from {:?}!", state),
|
||
_ => count += 1,
|
||
}
|
||
// ... this.
|
||
let mut count = 0;
|
||
if let UsCoin::Quarter(state) = coin {
|
||
println!("State quarter from {:?}!", state);
|
||
} else {
|
||
count += 1;
|
||
}
|
||
|
||
println!();
|
||
}
|
||
|
||
fn chapter_7() {
|
||
println!("Chapter 7: Modules");
|
||
mod_and_the_filesystem();
|
||
controlling_visibility_with_pub();
|
||
referring_to_names_in_differents_modules();
|
||
|
||
println!();
|
||
}
|
||
|
||
fn mod_and_the_filesystem() {
|
||
println!("Modules creates namespaces. We can access module's functions with `::`.");
|
||
println!("We can nest modules. Modules can have the same names and functions if they don't have the same hierarchy.");
|
||
println!("Modules can be implementerd inline or declared and the implementation is deported in an external file.");
|
||
println!("The external file should be named with the name of the module: `mod toto;` => toto.rs");
|
||
println!("If the module has sub-modules, instead of a file we create a folder named exactly like the module with a mod.rs file in it.");
|
||
println!("This structure may be recursive.");
|
||
|
||
println!();
|
||
}
|
||
|
||
fn controlling_visibility_with_pub() {
|
||
println!("By default, modules and functions are privates. We use the `pub` keyword to make them public.");
|
||
println!("If an item is public, it can be accessed though any of its parent modules.");
|
||
println!("If an item is private, it can be accessed only by its immediate parent module and any of the parent's child modules.");
|
||
|
||
println!();
|
||
}
|
||
|
||
fn referring_to_names_in_differents_modules() {
|
||
println!("The `use` keyword bring a module, a function or an enum variant into the scope.");
|
||
println!("We can bring multiple item on the scope with a curly bracket list, or all of the item with the `*` glob operator.");
|
||
// See below for a complex / coplete example.
|
||
println!("We can reference an element with an absolute path: `::client::connect()`.");
|
||
println!("We can reference an element with an relative path to the parent: `super::client::connect()`.");
|
||
|
||
println!();
|
||
}
|
||
|
||
#[allow(unused_imports, dead_code)]
|
||
// Preparation
|
||
mod foo {
|
||
pub mod bar {
|
||
pub type Foo = ();
|
||
}
|
||
pub mod baz {
|
||
pub mod quux {
|
||
pub type Bar = ();
|
||
}
|
||
}
|
||
}
|
||
|
||
#[allow(unused_imports, dead_code)]
|
||
// Importing into scope
|
||
use foo::{
|
||
bar::{self, Foo},
|
||
baz::{*, quux::Bar},
|
||
};
|
||
|
||
fn chapter_8() {
|
||
println!("Chapter 8: Common collections");
|
||
vectors();
|
||
strings();
|
||
hash_maps();
|
||
|
||
println!("Exercice time 😺");
|
||
|
||
println!();
|
||
}
|
||
|
||
fn vectors() {
|
||
println!("Vectors");
|
||
let mut v = vec![1, 2, 3];
|
||
v.push(4);
|
||
|
||
{
|
||
// let does_not_exist = &v[100]; // panic
|
||
let does_not_exist = v.get(100); // Option<&T>
|
||
println!("We can get a vector value with `&v[i]` or `v.get(i)`: {:?}", does_not_exist);
|
||
}
|
||
|
||
for i in &v {
|
||
println!("{}", i);
|
||
}
|
||
|
||
for i in &mut v {
|
||
*i += 50;
|
||
}
|
||
|
||
for i in &v {
|
||
println!("{}", i);
|
||
}
|
||
|
||
#[derive(Debug)]
|
||
enum SpreadsheetCell {
|
||
Int(i32),
|
||
Float(f64),
|
||
Text(String),
|
||
}
|
||
let row = vec![
|
||
SpreadsheetCell::Int(3),
|
||
SpreadsheetCell::Text(String::from("blue")),
|
||
SpreadsheetCell::Float(13.37),
|
||
];
|
||
|
||
println!("To have multiples types inside a vector we should use an enum: {:?}.", row);
|
||
|
||
println!();
|
||
}
|
||
|
||
fn strings() {
|
||
let hello = String::from("السلام عليكم");
|
||
println!("Hello: {}", hello);
|
||
let hello = String::from("Dobrý den");
|
||
println!("Hello: {}", hello);
|
||
let hello = String::from("Hello");
|
||
println!("Hello: {}", hello);
|
||
let hello = String::from("שָׁלוֹם");
|
||
println!("Hello: {}", hello);
|
||
let hello = String::from("नमस्ते");
|
||
println!("Hello: {}", hello);
|
||
let hello = String::from("こんにちは");
|
||
println!("Hello: {}", hello);
|
||
let hello = String::from("안녕하세요");
|
||
println!("Hello: {}", hello);
|
||
let hello = String::from("你好");
|
||
println!("Hello: {}", hello);
|
||
let hello = String::from("Olá");
|
||
println!("Hello: {}", hello);
|
||
let hello = String::from("Здравствуйте");
|
||
println!("Hello: {}", hello);
|
||
let hello = String::from("Hola");
|
||
println!("Hello: {}", hello);
|
||
|
||
let s1 = String::from("Hello, ");
|
||
let s2 = String::from("world!");
|
||
let s3 = s1 + &s2;
|
||
println!("s1 is moved and not usable anymore, s2: {}, s3: {}", /*s1, */s2, s3);
|
||
let s2 = String::from("tac");
|
||
|
||
let s1 = String::from("tic");
|
||
let s3 = String::from("toe");
|
||
|
||
let s = s1 + "-" + &s2 + "-" + &s3;
|
||
println!("s: {}", s);
|
||
|
||
let s1 = String::from("tic"); // s1 was moved because of the `+` operator
|
||
|
||
let s = format!("{}-{}-{}", s1, s2, s3);
|
||
println!("s: {}, s1 {}, s2 {}, s3 {}", s, s1, s2, s3); // here no String is moved
|
||
|
||
println!("We cannot use index on Strings but we can refer to a range.");
|
||
println!("But we should be carefull with the ranges to not split a 2byte unicode char.");
|
||
|
||
let hindi_2bytes_with_modifiers = "नमस्ते";
|
||
|
||
println!("Individuals bytes:");
|
||
for b in hindi_2bytes_with_modifiers.bytes() {
|
||
println!("{}", b);
|
||
}
|
||
|
||
println!("Individuals UTF-8 chars. Note the modifier (accents, ...) may be separated from the naked character.");
|
||
for c in hindi_2bytes_with_modifiers.chars() {
|
||
println!("{}", c);
|
||
}
|
||
|
||
println!("If we want the human characteres insteads of the UTF-8 ones -> https://crates.io/");
|
||
|
||
println!();
|
||
}
|
||
|
||
fn hash_maps() {
|
||
use std::collections::HashMap;
|
||
|
||
let mut scores = HashMap::new();
|
||
|
||
scores.insert(String::from("Blue"), 10);
|
||
scores.insert(String::from("Yellow"), 50);
|
||
|
||
println!("Scores: {:?}", scores);
|
||
|
||
let teams = vec![String::from("Blue"), String::from("Yellow")];
|
||
let initial_scores = vec![10, 50];
|
||
|
||
let scores_tuple = teams.iter().zip(initial_scores.iter());
|
||
println!("Scores tuple: {:?} (Intermediate not necessary, just to see what is happening)", scores_tuple);
|
||
|
||
let scores: HashMap<_, _> = scores_tuple.collect();
|
||
println!("Scores: {:?}", scores);
|
||
|
||
let team_name = String::from("Blue");
|
||
match scores.get(&team_name) {
|
||
Some(sc) => println!("{} team score is: {}", &team_name, sc),
|
||
None => println!("No score for team: {}", &team_name),
|
||
}
|
||
|
||
for (key, value) in &scores {
|
||
println!("{}: {}", key, value);
|
||
}
|
||
|
||
let mut scores = HashMap::new();
|
||
|
||
scores.insert(String::from("Blue"), 10);
|
||
scores.insert(String::from("Yellow"), 50);
|
||
|
||
scores.insert(String::from("Blue"), 25); // Overwriting a value
|
||
|
||
for (key, value) in &scores {
|
||
println!("{}: {}", key, value);
|
||
}
|
||
|
||
scores.entry(String::from("Blue")).or_insert(50); // Only insert if the value does not exist
|
||
scores.entry(String::from("Red")).or_insert(50);
|
||
println!("{:?}", scores);
|
||
|
||
let text = "hello world wonderful world";
|
||
let mut map = HashMap::new();
|
||
|
||
for word in text.split_whitespace() {
|
||
let count = map.entry(word).or_insert(0);
|
||
*count += 1;
|
||
}
|
||
println!("{:?}", map);
|
||
|
||
println!("The hashing function is cryptographically secure. If it's slow, use an other one implementing the `BuildHasher` trait.");
|
||
|
||
println!();
|
||
}
|
||
|
||
fn chapter_9() {
|
||
unrecoverable_errors_with_panic();
|
||
recoverable_errors_with_result();
|
||
panic_or_result();
|
||
|
||
println!();
|
||
}
|
||
|
||
fn unrecoverable_errors_with_panic() {
|
||
println!("The `panic!` macro crash the program gracefully. We may configure it to abort instead.");
|
||
println!("If we want the stacktrace we need to set the environment variable: `RUST_BACKTRACE`.");
|
||
|
||
println!();
|
||
}
|
||
|
||
fn recoverable_errors_with_result() {
|
||
println!("The `Result` enum enable a function to return a recoverable error.");
|
||
println!("We may check for a kind of error by stacking the `match`.");
|
||
println!("Instead we may prefer to use closures to better communicate our intention and not use to much nested `match`.");
|
||
println!("For example `unwrap` and `expect` are shortcuts to panic on error.");
|
||
println!("See chapter 13 for more infos.");
|
||
|
||
println!("If we want to let the caller take care of the errors we encounter we can propagate the error with `?`");
|
||
println!("Fun example (ending with a bit of a cheat): https://doc.rust-lang.org/stable/book/2018-edition/ch09-02-recoverable-errors-with-result.html#propagating-errors");
|
||
|
||
println!();
|
||
}
|
||
|
||
fn panic_or_result() {
|
||
println!("It is ok to panic! when in `Examples, Prototype Code, and Tests`");
|
||
println!("For the examples and when prototypying `unwrap` and `expect` are clear markers showing where to handle the errors");
|
||
println!("When testing panicking is the way to tell that the test failed");
|
||
|
||
println!("When we hardcode a value (or have more infos than the compiler)");
|
||
println!("We can also panic when the developper using our library break one of our contract.");
|
||
println!("Each time we create contract like this we should document them.");
|
||
// Example: Trying to access an array/Vec<> after a it's max length (out-of-bounds)
|
||
|
||
println!();
|
||
}
|