Hi Rust!

> whoami

michele.damico@gmail.com

https://github.com/la10736

https://www.linkedin.com/in/damico/

@PhenoCoder

Perchè RUST?

  • Pericolosi (non Type Safe)
  • Muti-Threading difficile e Error prone

C/C++

Obiettivi di RUST

  • Veloce
  • Astrazzione a costo zero
  • Accesso sicuro alla memoria (Type Safe)
  • Vietare i Data-Race in compilazione
  • Eliminare (il più possibile) i comportamenti indefiniti

Ecosistema

  • rustc
  • cargo
  • rustup
  • crates.io 
michele@DartVader:~/bite_of_rust$ cargo init hello --bin
     Created binary (application) project
michele@DartVader:~/bite_of_rust$ cd hello/
michele@DartVader:~/bite_of_rust/hello$ cargo run
   Compiling hello v0.1.0 (file:///home/michele/bite_of_rust/hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.54 secs
     Running `target/debug/hello`
Hello, world!
michele@DartVader:~/bite_of_rust/hello$ echo "
#[test]
fn ok() { assert!(true) }
#[test]
fn ko() { assert!(false) }
" >> src/main.rs 
michele@DartVader:~/bite_of_rust/hello$ cargo test
   Compiling hello v0.1.0 (file:///home/michele/bite_of_rust/hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.37 secs
     Running target/debug/deps/hello-e41301c0dacd96ed

running 2 tests
test ok ... ok
test ko ... FAILED

failures:

---- ko stdout ----
	thread 'ko' panicked at 'assertion failed: false', src/main.rs:8
note: Run with `RUST_BACKTRACE=1` for a backtrace.


failures:
    ko

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured

error: test failed

Alcune Caratteristiche

  • Variabili (move, copy, borrowing)

  • Ownership

  • Mutabilità per istanza

  • inferenza di tipo

  • Gestione stringhe
  • Gestione memoria

  • ....

Alcune Caratteristiche (Cont)

  • Enum ++(+)

  • Gestione Errori (no eccezioni)

  • Derivano solo le interfacce
  • Generic (Trait) con bound

  • Destrutturazione (pattern)

  • Closure e First-Class functions

  • macro (hygenic)

Sicuro e Veloce... Ma come?

Senza garbage collector come fa a gestire la memoria?

fn destroy_example() {
    let mut x: &i32;
    let y = 42;

    x = &y;
}
error[E0597]: `y` does not live long enough
 --> src/main.rs:8:1
  |
7 |     x = &y;
  |          - borrow occurs here
8 | }
  | ^ `y` dropped here while still borrowed
  |
  = note: values in a scope are dropped in the opposite order they are created

Sicuro e Veloce... Ma come?

Ownership

struct S { a: i32 }

fn creator() {
    let s = S { a: 12 };
    take_ownership(s);
    println!("a={}", s.a);
}

fn take_ownership(data: S) {
}
error[E0382]: use of moved value: `s.a`
 --> src/main.rs:7:22
  |
6 |     take_ownership(s);
  |                    - value moved here
7 |     println!("a={}", s.a);
  |                      ^^^ value used here after move
  |
  = note: move occurs because `s` has type `S`, which does not implement the `Copy` trait
fn creator() {
    let s = S { a: 12 };
    borrowing(&s);
    println!("creator ... a={}", s.a);
}

fn borrowing(data: &S) {
    println!("borrowing ... a={}", data.a);
}

Borrowing

Per quanto riguarda il data race?

fn mutability_and_refernce() {
    let mut a = 42;
    let mut_ref = &mut a;
    let other_ref = &a;
}
error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
 --> src/main.rs:7:22
  |
6 |     let mut_ref = &mut a;
  |                        - mutable borrow occurs here
7 |     let other_ref = &a;
  |                      ^ immutable borrow occurs here
8 | }
  | - mutable borrow ends here
fn mutabilitu_and_refernce() {
    let mut a = 42;
    let other_ref = &a;
    a = 15;
}
error[E0506]: cannot assign to `a` because it is borrowed
 --> src/main.rs:7:5
  |
6 |     let other_ref = &a;
  |                      - borrow of `a` occurs here
7 |     a = 15;
  |     ^^^^^^ assignment to borrowed `a` occurs here

Cosa ci guadagno?

my_list = [1, 3, 5, 7, 9]
for index, val in enumerate(my_list):
    if val > 4:
        del my_list[index]
print("Output: ")
print(my_list)

Output: 
[1, 3, 7]
fn main() {
    let mut my_vec = vec![1, 3, 5, 7, 9];
    for (index, &val) in my_vec.iter().enumerate() {
        if val > 4 {
            my_vec.remove(index);
        }
    }
    println!("Output:\n{:?}", my_vec);
}

... non compila ...

   Compiling vspython_remove v0.1.0 (file:///home/michele/learning/rust/vspython_remove)
error[E0502]: cannot borrow `my_vec` as mutable because it is also borrowed as immutable
 --> src/main.rs:5:13
  |
3 |     for (index, &val) in my_vec.iter().enumerate() {
  |                          ------ immutable borrow occurs here
4 |         if val > 4 {
5 |             my_vec.remove(index);
  |             ^^^^^^ mutable borrow occurs here
6 |         }
7 |     }
  |     - immutable borrow ends here

error: aborting due to previous error

... dobbiamo fare i bravi!

fn main() {
    let mut my_vec = vec![1, 3, 5, 7, 9];
    my_vec = my_vec.into_iter().filter(|val| *val <= 4).collect();
    println!("Output:\n{:?}", my_vec);
}
/home/michele/.cargo/bin/cargo run --color=always
   Compiling vspython_remove v0.1.0 (file:///home/michele/learning/rust/vspython_remove)
    Finished dev [unoptimized + debuginfo] target(s) in 0.49 secs
     Running `target/debug/vspython_remove`
Output:
[1, 3]

Process finished with exit code 0

Thread safe?

import threading
import time

N = 10000
a = [0]
threads = []

def update():
    v = a[0]
    time.sleep(0)
    a[0] = v + 1

for i in range(N):
    t = threading.Thread(target=update)
    t.start()
    threads.append(t)

for t in threads:
    t.join()

assert a[0] == N, "a = {} != N = {}".format(a[0], N)
import kotlin.concurrent.thread

fun main(args : Array<String>) {
    val N = 10000
    var a = 0
    val threads = (1..N).map {
        thread { a += 1 }
    }
    threads.map { it.join() }

    assert(a == N) {
        "$a != $N"
    }
}
Traceback (most recent call last):
  File "/home/michele/learning/python/concurrent/concurrent.py", line 21, in <module>
    assert a[0] == N, "a = {} != N = {}".format(a[0], N)
AssertionError: a = 9988 != N = 10000
Exception in thread "main" java.lang.AssertionError: 9998 != 10000
	at ConcurrentKt.main(concurrent.kt:11)

RUST

use std::thread::spawn;
use std::sync::{Arc, Mutex};

fn main() {
    static N: usize = 10000;
    let r_val = Arc::new(Mutex::new(0));
    let mut handlers: Vec<_> = Default::default();

    for _ in 0..N {
        let r_val = r_val.clone();
        handlers.push(spawn(move || {
            let mut r_val = r_val.lock().unwrap();
            *r_val += 1;
        }
        ));
    }
    for h in handlers {
        h.join().unwrap();
    }
    let result = r_val.lock().unwrap().clone();
    print!("Sum = {}", result);
    assert_eq!(N, result);
}
   Compiling concurrent2 v0.1.0 ......
    Finished dev [unoptimized ...
     Running `target/debug/concurrent2`
Sum = 10000
Process finished with exit code 0

Come abbiamo fatto?

use std::thread::spawn;

fn main() {
    static N: usize = 10000;
    let mut val = 0;
    let r_val = &mut val;
    let mut handlers: Vec<_> = Default::default();

    for _ in 0..N {
        handlers.push(spawn(|| {
            *r_val += 1;
        }
        ));
    }
    for h in handlers {
        h.join().unwrap();
    }
    print!("Sum = {}", val);
    assert_eq!(N, val);
}
   Compiling concurrent2 v0.1.0 (file:///home/michele/learning/rust/concurrent2)
error: `val` does not live long enough
  --> src/main.rs:6:22
   |
6  |     let r_val = &mut val;
   |                      ^^^ does not live long enough
...
20 | }
   | - borrowed value only lives until here
   |
   = note: borrowed value must be valid for the static lifetime...

error[E0373]: closure may outlive the current function, but it borrows `r_val`, which is owned by the current function
  --> src/main.rs:10:29
   |
10 |         handlers.push(spawn(|| {
   |                             ^^ may outlive borrowed value `r_val`
11 |             *r_val += 1;
   |              ----- `r_val` is borrowed here
   |
help: to force the closure to take ownership of `r_val` (and any other referenced variables), use the `move` keyword, as shown:
   |         handlers.push(spawn(move || {

error: aborting due to 2 previous errors

.... Si arrabbia un casino

Assecondiamo...

for _ in 0..N {
    handlers.push(spawn(move || {
        *r_val += 1;
    }
    ));
}

usiamo move come suggerito

   Compiling concurrent2 v0.1.0 (file:///home/michele/learning/rust/concurrent2)
error: `val` does not live long enough
  --> src/main.rs:6:22
   |
6  |     let r_val = &mut val;
   |                      ^^^ does not live long enough
...
20 | }
   | - borrowed value only lives until here
   |
   = note: borrowed value must be valid for the static lifetime...

error[E0382]: capture of moved value: `r_val`
  --> src/main.rs:11:14
   |
10 |         handlers.push(spawn(move || {
   |                             ------- value moved (into closure) here
11 |             *r_val += 1;
   |              ^^^^^ value captured here after move
   |
   = note: move occurs because `r_val` has type `&'static mut usize`, which does not implement the `Copy` trait

error: aborting due to 2 previous errors

...Reference Counting?

use std::thread::spawn;

fn main() {
    static N: usize = 10000;
    let r_val = std::rc::Rc::new(0);
    let mut handlers: Vec<_> = Default::default();

    for _ in 0..N {
        let r_val = r_val.clone();
        handlers.push(spawn(move || {
            *r_val += 1;
        }
        ));
    }
    for h in handlers {
        h.join().unwrap();
    }
    print!("Sum = {}", r_val);
    assert_eq!(N, *r_val);
}
   Compiling concurrent2 v0.1.0 (file:///home/michele/learning/rust/concurrent2)
error[E0277]: the trait bound `std::rc::Rc<usize>: std::marker::Send` is not satisfied in 
                    `[closure@src/main.rs:10:29: 12:10 r_val:std::rc::Rc<usize>]`
  --> src/main.rs:10:23
   |
10 |         handlers.push(spawn(move || {
   |                       ^^^^^ within `[closure@src/main.rs:10:29: 12:10 r_val:std::rc::Rc<usize>]`, 
                                    the trait `std::marker::Send` is not implemented for `std::rc::Rc<usize>`
   |
   = note: `std::rc::Rc<usize>` cannot be sent between threads safely
   = note: required because it appears within the type `[closure@src/main.rs:10:29: 12:10 r_val:std::rc::Rc<usize>]`
   = note: required by `std::thread::spawn`

error: aborting due to previous error

Rc non è thread safe :(

...Asincrono?

use std::thread::spawn;

fn main() {
    static N: usize = 10000;
    let r_val = std::sync::Arc::new(0);
    let mut handlers: Vec<_> = Default::default();

    for _ in 0..N {
        let r_val = r_val.clone();
        handlers.push(spawn(move || {
            *r_val += 1;
        }
        ));
    }
    for h in handlers {
        h.join().unwrap();
    }
    print!("Sum = {}", r_val);
    assert_eq!(N, *r_val);
}
   Compiling concurrent2 v0.1.0 (file:///home/michele/learning/rust/concurrent2)
error: cannot assign to immutable borrowed content
  --> src/main.rs:11:13
   |
11 |             *r_val += 1;
   |             ^^^^^^^^^^^ cannot borrow as mutable

error: aborting due to previous error

Ci siamo quasi ma clone() torna un

valore non mutabile ....

Arc al posto di Rc ...

mut(able) o mut(ex) ?

use std::thread::spawn;

fn main() {
    static N: usize = 10000;
    let r_val = std::sync::Arc::new(std::sync::Mutex::new(0));
    let mut handlers: Vec<_> = Default::default();

    for _ in 0..N {
        let r_val = r_val.clone();
        handlers.push(spawn(move || {
            let mut r_val = r_val.lock().unwrap();
            *r_val += 1;
        }
        ));
    }
    for h in handlers {
        h.join().unwrap();
    }
    let result = r_val.lock().unwrap().clone();
    print!("Sum = {}", result);
    assert_eq!(N, result);
}

Wrappiamo il valore con un mutex

   Compiling concurrent2 v0.1.0 ......
    Finished dev [unoptimized ...
     Running `target/debug/concurrent2`
Sum = 10000
Process finished with exit code 0

WOW!!!!!!!!!!!!!!

GRAZIE!

Special Guests...

Hi Rust!

By Michele D'Amico

Hi Rust!

  • 193