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 cargo install rustfmt 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;

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)

(C#), (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

și indică spre valori diferite Programatorul nu știe că p și q indică spre valori diferite

și indică spre valori diferite Fortran încă e folosit în 2017

Vezi restrict în C (dar nu C++)

în C (dar nu C++) 🎗️Părțile programelor trebuie să aibă grijă să nu modifice aceleași valori🎗️

În Rust valorile au: un owner, dar pot fi împrumutate ca referințe oricâte referințe imutabile, sau 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

vs. Java 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 Pot fi apelate funcții scrise în C

Pot fi exportate funcții pentru alte limbaje

bindgen

Rust QT Binding Generator

Performanță Performanță comparabilă cu C, fără garbage collector sau run-time

ripgrep

serde_json

csv

webrender

pathfinder

tantivy

rtfm

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

Comunitate Diverse locații pentru suport (IRC, Gitter, Discourse)

Comunitate foarte prietenoasă

Proiecte open-source