16/12/2022

Loïc BRANSTETT

loic.branstett@epitech.eu

60 minutes for the basics

(mostly)

feel free to ask questions

Rust?

Reliable

Performant

Productive

(no memory mis-use)

(think C++)

(think  🤖)

Rust's path?

Control vs. Safety

CONTROL

SAFETY

C

C++

Go

Python

Java

PHP

Haskell

Rust

C#

This Dark Magic

Ownership and Borrowing

Explaining your intent at compile-time

Immutability

Concept #1

let x = 5;
let x = 5;
x = 2;
error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:3:5
  |
2 |     let x = 5;
  |         -
  |         |
  |         first assignment to `x`
  |         help: consider making this binding mutable: `mut x`
3 |     x = 2;
  |     ^^^^^ cannot assign twice to immutable variable

Immutability

Concept #1

let mut x = 5;
x = 2;

⚠️️ Immutability != Constant ⚠️

Concept #2

Everything is expressions

let my_integer = { 5 };
let my_complex_algorthim = {
    let c = 20 + 2;
    c + 20
};
{ }

Concept #3

Shadowing

let x = 5;      // 1st declaration
let x = 5 + x;  // 2nd declaration
// In a CLI or web-app, one could do something like this

let age = app.get("age"); // age is a String
let age: i32 = age.parse().expect("unbale to convert to i32");

Concept #3

Shadowing

let mut espaces = "    ";
// try to assign a integer to a string.
// will not work because the type are different.
espaces = espaces.len();
let espaces = "     ";
let espaces = espaces.len();

Concept #4

Ownership

Concept #4

Ownership

  • Each value in Rust has an owner.
  • There can only be one owner at a time.
  • When the owner goes out of scope, the value will be dropped.

Concept #4

Ownership

  • Each value in Rust has an owner.
let a = 5;  // here 5 is owned by `a`

5; // here 5 is owned by an anonymous variable

let _anon_5 = 5;

Concept #4

Ownership

  • There can only be one owner at a time.
let a = 5;
let b = a;

println!("{a}"); // error: a no longer exist, it's value is now owned by b
println!("{b}"); // no problem

Concept #4

Ownership

  • When the owner goes out of scope, the value will be dropped.
{
    let a = 5;
}

// in reality
{
    let a = 5;
    drop(a);
}

Concept #5

Borrowing

Concept #5

Borrowing

🎉 References 🎉

&

&mut

Concept #5

Borrowing

let a = 5;
let b = &a;

println!("{b}");
let mut a = 5;
let b = &mut a;

println!("{b}");

Concept #5

Borrowing

let a = 5;
let b = &a;
let c = &a;

println!("{a} {b} {c}");

Concept #5

Borrowing

let mut a = 5;
let b = &mut a;
let c = &mut a;

println!("{a} {b} {c}");
error[E0499]: cannot borrow `a` as mutable more than once at a time
 --> src/main.rs:4:9
  |
3 | let b = &mut a;
  |         ------ first mutable borrow occurs here
4 | let c = &mut a;
  |         ^^^^^^ second mutable borrow occurs here
5 |
6 | println!("{a} {b} {c}");
  |                - first borrow later used here

Concept #5

Borrowing

&

&mut

&

&

&

&

XOR

Abbrev #1

Function

fn my_function(x: i32) -> i32 {
    x + 2
}

fn my_print_int(x: i32) {
    println!("{x}");
}

Concept #6

Lifetimes

fn dangle() -> &i32 {
    let s = 15;
    &s
}
fn dangle() -> &i32 { // dangle returns a reference to a i32

    let s = 15; // s is a new integer

    &s // we return a reference to the i32, s
} // Here, s goes out of scope, and is dropped.
  // Its memory goes away.
  // Danger!

Concept #6

Lifetimes

fn ref(x: &i32) -> &i32 {
    x
}

fn ref_explicit<'a>(x: &'a i32) -> &'a i32 {
    a
}

'something

It's time for

kahoot.it

Data types

Signed integer

Type Min Max
i8 -128 127
i16 -32_768 32_767
i32 -2_147_483_64 2_147_483_63
i64 -9_223_372_036_854_775_808 or -2^63 9_223_372_036_854_775_807 or 2^63-1
i128 -170_141_183_460_469_231_731_687_303_715_884_105_728 or -2^127 170_141_183_460_469_231_731_687_303_715_884_105_727 or 2^127-1

Data types

Unsigned integer

Type Min Max
u8 0 255
u16 0 65_535
u32 0 4_294_967_295
u64 0 18_446_744_073_709_551_615 or 2^64-1
u128 0 340_282_366_920_938_463_463_374_607_431_768_211_455 or 2^128-1

Abbrev #2

overflow

fn main() {
    let a: i8 = 128; // ERROR: overflow
}
error: literal out of range for `i8`
 --> test.rs:2:16
  |
2 |     let a: i8 = 128;
  |                 ^^^
  |
  = note: `#[deny(overflowing_literals)]` on by default
  = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127`

error: aborting due to previous error

Abbrev #3

gcc madness

#include <stdint.h>

int main(void) {
  int8_t a = 128; // int8_t signed on 8bits
  return 0;
}
$ gcc -Wall -Wextra

⚠️ No warnings !!! ⚠️

Abbrev #3

gcc madness

$ gcc -Wall -Wextra -Wstrict-overflow -Wconversion test.c
test.c: In function ‘main’:
test.c:5:14: warning: conversion from ‘int’ to ‘int8_t’ {aka ‘signed char’} changes value from ‘128’ to ‘-128’ [-Wconversion]
    5 |   int8_t a = 128;
      |              ^~~

Data types

Floating point numbers

f32 and f64

pub const PI: f32 = 3.14159274f32;
pub const PI: f32 = 3.14159274_f32;

Data types

Boolean

Simply bool! With true/false;

/// Returns `true` if `temperature` less or equal to 30°C
/// and greater than 19°C.
fn is_temperature_ok_for_working(temperature: i32) -> bool {
    temperature < 30 && temperature >= 19
}

Data types

Boolean

️But remember:

⚠️ no implicit conversion! ⚠️

let a = if 5 { '👻' } else { '🦀' };
error[E0308]: mismatched types
 --> test.rs:2:16
  |
2 |     let a = if 5 { '👻' } else { '🦀' };
  |                ^ expected `bool`, found integer

error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.

Data types

Character

char!

but size_of::<char>() = 32

char = Unicode Scalar Point

it's complicated, there is no one thruth defintion of what a character is, so Rust just used the closest avalaible thing

Data types

Tuple

(), (T1), (T1, T2) or (T1, T2, ...)

let a = ('🤖', 3i32); // tuple of (char, i32)
let c = (); // empty type () aka unit type

Function without return type explicitly return a unit! 🤖

Data types

String

&str

Borrowed variant

Owned variant

or String

Data types

Option<T>

pub enum Option<T> {
    None,
    Some(T),
}

Data types

Option<T>

fn is_power_of_two(x: u32) -> Option<u32> {
    if x & 1 == 0 {
    	Some(x)
    } else {
    	None
    }
}

Data types

Result<T, E>

pub enum Result<T, E> {
    Ok(T),
    Err(E),
}

Data types

&[T] / slice

let a = [1, 2, 3, 4, 5]; // array

let slice = &a[1..3]; // slice

assert_eq!(slice, &[2, 3]);

Reference to a contiguous sequence of elements

Data types

Vec

let mut a = Vec::new(); // vec
a.extend([1, 2, 3, 4, 5]);

let slice = &a[1..3]; // slice

assert_eq!(slice, &[2, 3]);

(Owned) Contiguous sequence of elements

Custom type

Struct

pub struct MyStruct {
    x: i32,
    yz: (i32, i64),
    pub(crate) print: Option<bool>
}
fn my_struct() -> MyStruct {
    let x = 5;
    MyStruct { x, yz: (1, 2), print: Some(true) }
}

Custom type

Enum

pub enum MyEnum {
    Hugo,
    Julien,
    Parot
}

pub fn my_enum() -> MyEnum {
    MyEnum::Julien
}

use MyEnum::Julien;

pub fn my_enum_2() -> MyEnum {
    Julien
}

Custom type

Enum

ADT (Algebric Data Type)

enum MyEnum {
    None,
    X(i32),
    YZ { x: i32, z: i32 },
    Other(MyStruct)
}

If/else

Control flow

if my_condition {
    // my code
} else if my_other_condition {
    // my other code
} else {
    // last resort, right?
}

If let

Control flow

let a = Some(...);

if a = ? { ... }
let a = Some(...);

if let Some(e) = a {
    println!("{e}");
}

Control flow

Match

let a = Some(...);

match a {
   Some(a) => dbg!(a),
   None => eprintln!("nothing"),
}

Control flow

let/else

let a = Some(5);

// ...

let Some(a) = a else {
    panic!("nothing!");
}

eprintln!("something!");

Control flow

return

fn my_int(a: i32) -> i32 {
    if a < 100 {
        return 1;
    }

    return a;
}

Looping

while

while my_condition {
    // in code
}


while my_int != 5 {
    println!("not equal to 5");
}

Looping

loop

while true {
    println!("looping...");
}

loop {
    println!("looping...");
}

Looping

for

for val in iterator {
    // ...
}

for a in &[1, 2, 3, 4, 5] {
    dbg!(a);
}

let v = vec![1, 2, 3];
for a in &v {
    dbg!(a);
}

(part 2)

It's time for

Tools

Cargo

Package manager

Build system

$ cargo check
$ cargo build
$ cargo run
// Cargo.toml
[package]
name = "my_package"
version = "0.1.0"

[dependencies]
serde = "1.0"
clap = "1.0.15"

Tools

rustfmt

$ cargo fmt
// or directly
$ rustfmt main.rs

Formatter of Rust code

Rust Style guidelines

Tools

rustdoc

Automatic documentation generator from source code

with doc-comment

/// This is a doc-comment
///
/// # It's markdown
///
/// It's also different from comment
/// because it has 3 slash
// this isn't a doc-comment
fn my_function() {}

Tools

rust-analyzer

Language Server Protocol

Ressources

Rust Book

RustLings

Rust By Exemple

Rust Standard Library

Questions?

Iterator

Definition

pub trait Iterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
    
    // ...
}

Iterator

Combination

trait Iterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
    
    fn count(self) -> usize { ... }
    fn last(self) -> Option<Self::Item> { ... }
    fn nth(&mut self, n: usize) -> Option<Self::Item> { ... }
    fn step_by(self, step: usize) -> StepBy<Self>ⓘ { ... }
    fn skip(self, n: usize) -> Skip<Self>ⓘ { ... }
    fn take(self, n: usize) -> Take<Self>ⓘ { ... }
    // ...    
}

Iterator

Exemple

let v = vec![1, 2, 3, 4, 5];

for a in v.iter()
          .filter(|a| a < 2)
          .nth(3)
{
    dbg!(a);
}


// will print: 2, 3, 4

Rust Basics

By urgau-1

Rust Basics

  • 109