A Bite of 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)

Tutta questa fatica per...

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)

E RUST?

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!!!!!!!!!!!!!!

String Calculator Kata

Modulo string_calculator con funzione add():

prende una stringa e torna un intero.

add("") == 0

String Calculator Kata

Data una stringa contenente un intero torna il numero intero rappresentato

add("3") == 3
add("5") == 5

String Calculator Kata

Somma i numeri separati da virgola

add("1,2") == 3
add("1,2,3") == 6

String Calculator Kata

Accetta anche \n come separatore

add("1\n2") == 3
add("1,3\n2,12,1\n4\n3,1") == 27

String Calculator Kata

La prima riga può contenere un header che definisce un separatore diverso

//[delimiter]\n[numbers…]

add("//[;]\n1;2") == 3
add("//[;]\n1;2\n3;4\n7") == 17

String Calculator Kata

I numeri negativi non sono ammessi: Se presenti mostrare l'errore

“negatives not allowed: <negatives>”

add("-1") == Err("negatives not allowed: -1")
add("1,-3,4,-2") == Err("negatives not allowed: -3,-2")

GRAZIE!

Special Guests...

A Bite of Rust

By Michele D'Amico

A Bite of Rust

  • 347