Použití programovacího jazyka Rust na produkčním systému

Presentations

Použití programovacího jazyka Rust na produkčním systému


Anotace

Jazyk Rust se stává mezi programátory stále populárnější alternativou k C++ na straně jedné a k jazykům vybavených automatickým správcem paměti (GC) na straně druhé. Na této přednášce si řekneme, které vlastnosti Rustu zjednodušují jeho použití v produkčních systémech, které knihovny se nejčastěji používají a jak se aplikace psané v Rustu zabezpečují.

images/rust_logo.png


Obsah přednášky


Požadavky na produkční jazyk v současnosti

images/real_world.jpg


Popularita a rozšířenost Rustu

Popularita Rustu

images/so1.png

images/so2.png

images/so3.png

Rozšířenost Rustu

images/stat_pypl.png

images/stat_openhub.png

images/rust_tiobe_index.png


Charakteristické rysy Rustu

images/rust_tree.png

images/npe.jpg


Charakteristické rysy Rustu


Charakteristické rysy Rustu


Rust versus C/C++

(D, Go, Nimrod…)


Rust versus Go

images/rust_go.png

Dobře, ale jedná se SKUTEČNĚ o konkurenty?

A samozřejmě oblíbené téma pro debaty…

Vybrané společné rysy jazyků Go a Rust

Porovnání Rustu a Go z hlediska vývojáře

Jazyk             Rust           Go
Přístup           moderní        konzervativní
Syntaxe           komplikovaná   jednoduchá, minimalistická
Učící křivka      menší sklon    větší sklon
Učící křivka      větší ampli.   menší amplituda
Rychlost překladu pomalejší      rychlejší
Backend           LLVM           vlastní
Linkování         static/dynamic přes -buildmode (//export!!!)
Rychlost kódu     rychlejší      pomalejší
Typový systém     rozsáhlý       bez generik
Neměnnost         explicitní     string, další přes rozhraní
Práce s pamětí    vlastnictví    GC
Detekce souběhu   ano            jen nepřímo
Závislosti        cargo          Go moduly

Rychlost přeložených aplikací

Více info


Některé význačné rysy Rustu

images/rust_logo.png

Komunikace s překladačem

Chybová hlášení překladače Rustu

error[E0382]: use of moved value: `c`
  --> an_example.rs:40:8
   |
39 |     funkce1(c);
   |        - value moved here
40 |     funkce2(c);
   |        ^ value used here after move
   |
   = note: move occurs because `c` has type `std::rc::Rc<Complex>`, which does not implement the `Copy` trait

Další příklad

fn foo(a int32) {
  println!(a)
}

fn main() {
  let x:i32 = 10
  foo(x);
}

images/rust_error.png


Datové typy

Odvození typů

// odvození typu všech proměnných (i z)
// použití makra println!
fn main() {
    let x = 6;
    let y = 7;
    let z;
    // překladač až nyní získá informace o typu
    z = x * y;
    println!("{} * {} = {}", x, y, z);
}

Typ Option

fn div(x: i32, y: i32) -> Option<i32> {
    if y != 0 {
        Some(x / y)
    } else {
        None
    }
}

fn main() {
    let z1 = div(2, 1);
    println!("{:?}", z1);

    let z2 = div(2, 0);
    println!("{:?}", z2);
}

Typ Result

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    if y != 0 {
        Ok(x / y)
    } else {
        Err("Divide by zero!")
    }
}

fn main() {
    let z1 = div(2, 1);
    println!("{:?}", z1);

    let z2 = div(2, 0);
    println!("{:?}", z2);
}

Typ Result a pattern matching

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    if y != 0 {
        Ok(x / y)
    } else {
        Err("Divide by zero!")
    }
}

fn print_div_result(result: Result<i32, &'static str>) {
    match result {
        Ok(value)  => println!("value: {}", value),
        Err(error) => println!("error: {}", error),
    }
}

fn main() {
    let z1 = div(2, 1);
    print_div_result(z1);

    let z2 = div(2, 0);
    print_div_result(z2);
}

Použití typu Result při výpočtech

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
    if y != 0 {
        Ok(x / y)
    } else {
        Err("Divide by zero!")
    }
}

fn print_div_result(result: Result<i32, &'static str>) {
    match result {
        Ok(value)  => println!("value: {}", value),
        Err(error) => println!("error: {}", error),
    }
}

fn inc(x: i32) -> i32 {
    x + 1
}

fn main() {
    let z0 = div(0, 1);
    print_div_result(z0);
    print_div_result(z0.map(inc));
    let z2 = div(2, 0);
    print_div_result(z2);
    print_div_result(z2.map(inc));
}

Anonymní funkce jsou hodnotami

fn main() {
    let is_odd = |x: i32| x & 1 == 1;
    //let is_even = |x: i32| !is_odd(x);
    let square = |x: i32| x*x;
    for x in 0..10 {
        println!("{}*{}={}, {} is {} number",
                 x, x, square(x), x, if is_odd(x) {"odd"} else {"even"})
    }
}

(Anonymní) funkce jsou hodnotami

fn main() {
    let is_odd = |x: i32| x & 1 == 1;
    //let is_even = |x: i32| !is_odd(x);
    let square = |x: i32| x*x;
    for x in 0..10 {
        println!("{}*{}={}, {} is {} number",
                 x, x, square(x), x, if is_odd(x) {"odd"} else {"even"})
    }
}

Zajímavé prvky jazyka

Tabulka faktoriálů

fn main() {
    for n in 1..10 {
        let fact = (1..n + 1).fold(1, |prod, x| prod * x);
        println!("{}! = {}", n, fact);
    }
}

Nekonečné sekvence

fn main() {
    let iter1 = 1..;
    let iter2 = iter1.filter(|x| x % 2 == 0);
    let iter3 = iter2.take(10);
    let suma  = iter3.fold(0, |sum, x| sum + x);
    println!("sum = {}", suma);
}

Pattern matching

// matching (nejjednodušší varianta)
fn main() {
    let x: i32 = 1;
    match x {
        0 => println!("zero"),
        1 => println!("one"),
        2 => println!("two"),
        3 => println!("three"),
        _ => println!("something else"),
    }
}

Pattern matching: složitější konstrukce

// matching, složitější ukázka
fn classify(x:i32) -> &'static str {
    match x {
        0         => "zero",
        1 | 2     => "one or two",
        3 | 4 | 5 => "from three to five",
        10 ... 20 => "from ten to twenty",
        _         => "something else",
    }
}

fn main() {
    for x in 0..10 {
        println!("{}:{}", x, classify(x))
    }
}

Přístup k OO v Rustu


Správa paměti

Box

fn main() {
    let x = Box::new(42);
    println!("{}", x);
}

let c = Box::new(Complex::new(1.0, 2.0));

// deref
fn print_complex(c: Box<Complex>) {
    println!("Complex number: {:}+{:}i", c.real, c.imag);
}

Rc

fn main() {
    println!("main begin");
    let c = Rc::new(Complex::new(0.0, 0.0));
    c.print();
    {
        println!("inner block begin");
        let c2 = Rc::new(Complex::new(0.0, 0.0));
        c2.print();
        {
            println!("inmost block begin");
            let c3 = Rc::new(Complex::new(0.0, 0.0));
            c3.print();
            println!("inmost block end");
        }
        println!("inner block end");
    }
    println!("main end");
}

// jeden sdílený objekt referencovaný třikrát
fn main() {
    println!("main begin");
    let c = Rc::new(Complex::new(0.0, 0.0));
    c.print();
    {
        println!("inner block begin");
        let c2 = c.clone();
        c2.print();
        {
            println!("inmost block begin");
            let c3 = c.clone();
            c3.print();
            println!("inmost block end");
        }
        println!("inner block end");
    }
    println!("main end");
}

Arc

fn start_threads() {
    let c = Arc::new(Complex::new(1.0, 1.0));
    for id in 0..10 {
        let owner = ComplexNumberOwner { id: id, value: c.clone() };
        // move protože objekt může přežít toto vlákno
        thread::spawn(move || {
                owner.print();
                delay(400);
        });
    }
}

Pole

fn main() {
    let array = [10, 20, 30, 40];
    // délka pole
    println!("array has {} items", array.len());
    // range + délka pole
    for i in 0..array.len() {
        println!("item #{} = {}", i + 1, array[i]);
    }
    // for-each
    for i in array.iter() {
        println!("{}", i);
    }
}
fn main() {
    let array = [1; 10];
    // délka pole
    println!("array has {} items", array.len());
    // range + délka pole
    for i in 0..array.len() {
        println!("item #{} = {}", i + 1, array[i]);
    }
    // for-each
    for i in array.iter() {
        println!("{}", i);
    }
}

Vektory

fn main() {
    let vector = vec![1, 2, 3, 4, 5];
    // délka vektoru
    println!("vector has {} items", vector.len());
    // range + délka pole
    for i in 0..vector.len() {
        println!("item #{} = {}", i + 1, vector[i]);
    }
    // for-each
    for item in vector.iter() {
        println!("{}", item);
    }
    // také funguje
    for item in &vector {
        println!("{}", item);
    }
}

„Slice“ polí a vektorů

fn main() {
    let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    let array2 = &array[2..6];
    for i in array2.iter() {
        println!("{}", i);
    }
}
fn main() {
    let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    let array2 = &array[5..];
    for i in array2.iter() {
        println!("{}", i);
    }
}
fn main() {
    let vector = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    println!("vector has {} items", vector.len());
    let slice = &vector[3..7];
    println!("slice has {} items", slice.len());
}

Vlákna

images/Amdahl.png

use std::thread;

fn main() {
    println!("Starting");
    for i in 1..10 {
        thread::spawn(move || {
            println!("Hello from a thread #{}", i);
        });
    }
    println!("Stopping");
}

Testování

“Pyramida” s různými typy testů


Jednotkové testy v Rustu

#[test]
fn ok_test() {
}

#[test]
fn failure() {
    assert!(false);
}

Další testy


Správce balíčků (Cargo)

images/cargo.png

Základní funkce

Statistika (tento týden)

Cargo.toml

[package]
name = "projectXYZ"
version = "0.1.0"
authors = ["Pepa z depa <pepa@openalt.cz>"]

[dependencies]
rand = "0.3.14"

Použití nástroje Cargo

$ cargo build
    Compiling project1 v0.1.0 (file:///home/tester/temp/project1)
    Finished debug [unoptimized + debuginfo] target(s) in 0.37 secs
$ cargo build
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
$ cargo run
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/project1`
Hello, world!

Použití nástroje Cargo

$ cargo test
   Compiling project1 v0.1.0 (file:///home/tester/temp/project1)
    Finished debug [unoptimized + debuginfo] target(s) in 0.43 secs
     Running target/debug/project1-b888664ab405e319

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

Použití nástroje Cargo

$ cargo install
   Compiling libc v0.2.17
   Compiling rand v0.3.14
   Compiling projectXYZ v0.1.0 (file:///home/tester/temp/projectXYZ)
    Finished release [optimized] target(s) in 5.88 secs
  Installing /home/tester/.cargo/bin/projectXYZ
warning: be sure to add `/home/tester/.cargo/bin` to your PATH to be able to run the installed binaries

Vybrané balíčky

Databáze

ORM

Data processing

Web

API a message brokery


Nasazení aplikací


Web Assembly


Rozhraní s Pythonem

#[no_mangle]
pub extern fn add_integers(x: i32, y: i32) -> i32 {
    x + y
}
#!/usr/bin/env python3
import ctypes
 
testlib1 = ctypes.CDLL("target/debug/libtest1.so")
 
result = testlib1.add_integers(1, 2)
print("1 + 2 = {}".format(result))
 
result = testlib1.add_integers(1.5, 2)
print("1.5 + 2 = {}".format(result))

Dokumentace


Odkazy