Rust

“Rust is a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety.”
  • Proiect personal al lui Graydon Hoare (2006)
  • Adoptat de Mozilla în 2009
  • Anunțat oficial în 2010
  • Self-hosted din 2011
  • Schimbări drastice în primii ani
  • Garbage-collected până în 2013
  • Versiunea 1.0 (stabilă) a apărut în mai 2015
  • Versiunile ulterioare sunt compatibile cu codul vechi
  • Azi: Rust 1.21 (octombrie 2017)
  • O versiune nouă la fiecare 6 săptămâni (!)
  • RFCs

rustup

  • Folosit pentru instalarea de toolchain-uri
  • rustup toolchain install stable-msvc
  • rustup toolchain install stable-apple-darwin
  • rustup component add rust-src rust-analysis rls

                    $ cargo new --bin hello
                     Created binary (application) `hello` project
                

                    $ cd hello
                    $ cargo run
                   Compiling hello v0.1.0 (file:///home/grayshade/hello)
                    Finished dev [unoptimized + debuginfo] target(s) in 0.34 secs
                     Running `target/debug/hello`
                    Hello, world!
                

                    $ tree
                    .
                    ├── Cargo.toml
                    └── src
                        └── main.rs
                

                    [package]
                    name = "hello"
                    version = "0.1.0"
                    authors = ["Laurentiu Nicola <lnicola@dend.ro>"]

                    [dependencies]
                

                    fn main() {
                        println!("Hello, world!");
                    }
                

Cargo

  • Interfața cu compilatorul și celelalte aplicații
  • cargo build
  • cargo run
  • cargo clean

rustfmt

  1. cargo install rustfmt
  2. cargo fmt

Crates


                    [dependencies]
                    cairo-rs = "0.2"
                    glib = "0.3"
                    num = "0.1"
                    rayon = "0.8.2"
                

                    extern crate cairo;
                    extern crate glib;
                    extern crate num;
                    extern crate rayon;
                

crates.io

Awesome Rust


                    fn main() {
                        let name = "Matilda";
                        let age = 2;
                        println!("Miau! Sunt {} și am {} ani.", name, age);
                    }
                
  • Sintaxă în mare parte inspirată din C și Ruby
  • println! verificat la compilare!

                    let mut balance = 100;
                    balance += 10;
                    println!("The account balance is {}", balance);
                

Variabilele (bindings!) sunt în general imutabile

Expresivitate, restricții

  • 🎗️Unele limbaje nu pot exprima anumite idei🎗️
  • 🎗️Există idei pe care vrem să nu le putem exprima🎗️

                    ArrayList<Integer> xoxo = new ArrayList<>();
                    xoxo.add(42);
                    xoxo.remove("why‽");
                

Sisteme de tipuri

  • static vs. dynamic, strong vs. weak
  • Clase de valori: numere întregi, șiruri de caractere, numere pare
  • Definesc operațiile care pot fi aplicate valorilor
  • 🎗️Scopul unui sistem de tipuri este să respingă programe greșite🎗️
  • i8, u8, i16, u16, i32, u32, usize
  • f32, f64
  • bool
  • char
  • String, &str
  • enum, struct &c.

Deducția tipurilor

  • Toate valorile au tipuri cunoscute la compilare
  • În general tipurile pot fi deduse
  • Adnotări
    let age: u8 = 2;
  • Tipurile sunt deduse doar pentru variabilele locale

Funcții


                    fn double(n: i32) -> i32 {
                        n * 2
                    }

                    fn main() {
                        println!("The answer is {}", double(21));
                    }
                

Structuri de control


                    let mut odds = 0;
                    let mut evens = 0;

                    for i in 0..100 {
                        if i % 2 == 0 {
                            evens += 1;
                        } else {
                            odds += 1;
                        }
                    }
                

Array-uri, vectori, slices

Play


                    let mut numbers = [10, 19, 30];
                    numbers[2] = 20;

                    let mut colours = vec!["red", "green", "blue"];
                    colours.push("fuchsia");

                    println!("Some colours I know are: {:?}", colours);
                    println!("But today I prefer: {:?}", &colours[0..2]);
                
  • Array-urile își cunosc lungimea
  • Slice-urile memorează o referință către valori și lungimea, dar nu pot accesa restul valorilor
  • Validitatea indicilor este verificată la execuție

Iteratori, closures


                    (0..100)
                      .map(|i| i * 3)
                      .filter(|i| i % 2 == 1)
                      .sum();
                
  • Valorile nu există simultan în memorie
  • Vezi IEnumerable (C#), Stream (Java)
  • Performanța?

Enumerări


                    enum Movement {
                        Walk(f32),
                        Turn(f32),
                        Jump
                    }

                    let movement = get_command();
                    match movement {
                        Movement::Walk(distance) => walk(distance),
                        Movement::Turn(angle) => turn(angle),
                        Movement::Jump => jump(),
                    }
                
  • Vezi tagged unions, variants
  • match este verificat de compilator

Match


                    let s = match x {
                        0 => "zero",
                        1 => "one",
                        2 => "a couple",
                        3 | 4 => "three",
                        5..10 => "many",
                        _ => "a lot",
                    };
                

Structuri

Play


                    #[derive(Debug)]
                    struct Point {
                        x: f32,
                        y: f32,
                    }

                    impl Point {
                        fn new(x: f32, y: f32) -> Self {
                            Point { x: x, y: y }
                        }
                    }

                    let p = Point::new(2.0, 8.0);
                    println!("{:?}", p);
                

Option, if let


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

                    let mut x = None;
                    x = Some(10);

                    if let Some(v) = x {
                    }
                

Valori opționale vs. null pointers

“I call it my billion-dollar mistake. [...] This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.” — C. A. R. Hoare

Destructuring bind, tupluri


                    let p = Some(Point::new(2.0, 8.0));

                    if let Some(Point { x, y }) = p {
                    }

                    match p {
                        Some(Point { x, y }) => {},
                        None => {},
                    }

                    let t = (10, "a");
                    let (n, s) = t;
                

Generics


                    enum Option<T> {
                        None,
                        Some(T),
                    }
                
  • Asemănătoare tipurilor generice din C#
  • Modelul din C++ (monomorfizare)
  • Parametrii generici nu pot fi valori, ci doar tipuri

Testare


                pub fn add_two(a: i32) -> i32 {
                    a + 2
                }

                #[bench]
                fn bench_add_two(b: &mut Bencher) {
                    b.iter(|| add_two(2));
                }
                

                    $ cargo bench
                    Compiling hello v0.1.0 (file:///home/grayshade/hello)
                        Finished release [optimized] target(s) in 0.74 secs
                        Running target/release/deps/hello-b644eaf5a2174734

                    running 1 test
                    test bench_add_two ... bench:           1 ns/iter (+/- 0)

                    test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out
                
 

Traits


                    trait Hello {
                        fn greet(&self);
                    }

                    struct Cat;

                    impl Hello for Cat {
                        fn greet(&self) {
                            println!("Meow meow meow meow meow");
                        }
                    }
                

Traits


                    struct Dog;

                    impl Hello for Dog {
                        fn greet(&self) {
                            println!("Woof");
                        }
                    }
                

Traits


                    fn meet<T: Hello>(whom: &T) {
                        whom.greet();
                    }

                    fn main() {
                        let cat = Cat;
                        let dog = Dog;

                        meet(&cat);
                        meet(&dog);
                    }
                

Play

  • OOP înseamnă altceva în fiecare limbaj
  • Pentru mulți, OOP înseamnă clase derivate — cu cât mai multe, cu atât mai bine (Java)
  • C++: moștenire multiplă și virtuală, ajustări de pointer-i, dynamic_cast, RTTI, destructori virtuali, ierarhii adânci de clase
  • Constructori de copiere vs. clone()
  • Composition over inheritance
  • Rust nu are moștenire (!)

Traits

  • Un trait este un contract pe care tipurile îl respectă
  • Similare cu interfețele, dar Rust monomorfizează tipurile generice
  • Pot fi implementate în afara claselor
  • Fără cost de performanță la dispatch static; inlining
  • Dispatch dinamic mai rapid — fat pointers
  • Vezi C++ contracts, extension methods, type classes

Error handling


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

                    fn read_file(file: &Path) -> Result<String, io::Error> {
                        let mut f = File::open(file)?;

                        let mut buffer = String::new();
                        f.read_to_string(&mut buffer)?;

                        Ok(buffer)
                    }
                

Error handling

  • Excepțiile din C++ complică limbajul și nu pot fi evitate
  • Exception safety guarantees
  • Constructori de copiere și mutare, destructori
  • Prea multe moduri de raportare a erorilor (errno, return values, std::error_code, excepții, setjmp)

                    foo(unique_ptr<C>(new C),
                        unique_ptr<C>(new C));
                

Spot the bug

Ownership

  • Valorile din Rust sunt mutate (consumate, transferate) la folosire
  • Sunt distruse la sfârșitul blocului de care aparțin (C++ RAII)
  • C++ move semantics

                    fn consume(_: String) {}

                    let s = String::from("hello");

                    consume(s);
                    consume(s);
                

Play

Quiz

Va fi f optimizată la g?


                    void f(int *p, int *q) {
                        *p += *q;
                        *p += *q;
                    }

                    void g(int *p, int *q) {
                        *p += *q + *q;
                    }
                

...


                    int x = 10, *p = &x, *q = &x;
                    f(p, q);

                    x = 10;
                    g(p, q);
                

Aliasing

  • Compilatorul nu știe că p și q indică spre valori diferite
  • Programatorul nu știe că p și q indică spre valori diferite
  • Fortran încă e folosit în 2017
  • Vezi restrict în C (dar nu C++)
  • 🎗️Părțile programelor trebuie să aibă grijă să nu modifice aceleași valori🎗️

În Rust valorile au:

  1. un owner, dar pot fi împrumutate ca referințe
  2. oricâte referințe imutabile, sau
  3. o singură referință mutabilă

🎗️aliasing XOR mutability🎗️


                    fn borrow(_s: &String) {}
                    fn mutate(_s: &mut String) {}

                    fn main() {
                        let mut s = String::from("hello ");

                        borrow(&s);
                        mutate(&mut s);

                        println!("{}", s);
                    }
                

Play

Threading

  • Cei care au folosit thread-uri recunosc restricțiile
  • race conditions
  • readers-writer locks
  • Firește, Rust oferă primitivele comune de sincronizare: mutex-uri, RW locks, condition variables, bariere, operații atomice, Once, shared pointers, canale

Canale


                    let (tx, rx) = mpsc::channel();

                    thread::spawn(move || for i in 0..10 {
                        tx.send(i).unwrap();
                    });

                    for x in rx.iter() {
                        println!("{}", x);
                    }
                

Play

“Anything at all can happen; the Standard imposes no requirements. The program may fail to compile, or it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended.”

ISO/IEC 9899:2011 §J.2

😱

  • Rust evită UB, prin specificarea lui și verificări la compilare și execuție
  • 🎗️Multe greșeli sunt erori de compilare în Rust: double frees, use-after-free, buffer overflows, race conditions🎗️
  • Rust e o alegere bună pentru parser-e, codec-uri sau servicii de rețea
 
  • Session types — automate finite codificate în sistemul de tipuri
  • Proofs: MutexGuard vs. Java synchronized
  • String, OsString, CString
  • Analiză dimensională

Macrouri

  • Modalități de extindere a sintaxei: macrouri declarative și procedurale, custom derive, compiler plugins
  • Build scripts — pentru generare de fișiere sursă la compilare

Interoperabilitate

Performanță

via


                    class ::String
                      def blank?
                        /\A[[:space:]]*\z/ == self
                      end
                    end
                

C


                    extern fast_blank(buf: &Buf) -> bool {
                        buf.as_slice().chars().all(|c| c.is_whitespace())
                    }
                
Ruby 946K iter/s
C 10.5M iter/s
Rust 11M iter/s
 

www.rust-lang.org/en-US/friends.html

“Most loved language” second year in a row

Documentație

doc.rust-lang.org/std/string/struct.String.html

Comunitate

  • Diverse locații pentru suport (IRC, Gitter, Discourse)
  • Comunitate foarte prietenoasă
  • Proiecte open-source

❤️