Rust-notes/src/main.rs

1041 lines
34 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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!();
}