Rust programming language

Presentations

Rust programming language

images/rust_logo.png


Table of content


Requirements to production-ready programming language

images/real_world.jpg


Rust popularity and expansion

Popularity of Rust

images/so1.png

images/so2.png

images/so3.png

Rust expansion

images/stat_pypl.png

images/stat_openhub.png

images/rust_tiobe_index.png


Rust characteristics

images/rust_tree.png

images/npe.jpg


Rust characteristics


Rust characteristics

images/object.jpg


Rust versus C/C++

(D, Nimrod, …)


Rust versus Go

images/rust_go.png

Ok, so are Go and Rust real competitors?

Commmon Rust and Go properties

Rust and Go comparisons from developer perspective

Language          Rust           Go
Approach          modern         conservative
Syntax            complicated    simple, minimalistic
Learning curve    lower slope    higher slove
Learning curve    higher maximum lower maximum
Compiler speed    slower         faster
Backend           LLVM           custom
Linking           static/dynamic via -buildmode (//export!!!)
Code speed        faster         slower
Type system       huge           without generics (yet)
Immutability      explicit       string, other via interfaces
Memory management ownership      GC
Race condition c. yes            not direcly
Dependency mngm.  cargo          Go modules

Final code speed

More information


Selected Rust properties

images/rust_logo.png


Communication with compiler

Error messages generated by Rust compiler

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

Second example - many beginners mistakes

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

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

images/rust_error.png


Data types

Type inference

// macro println!
fn main() {
    let x = 6;
    let y = 7;
    let z;
    // now compiler knows types of all variables
    z = x * y;
    println!("{} * {} = {}", x, y, z);
}

Data type 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);
}

Data type 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);
}

Data type Result and 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);
}

Usage of data type Result in computation

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));
}

Anonymous functions are values

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"})
    }
}

Anonymous functions are values (cont.)

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"})
    }
}

Other interesting parts of Rust

Factorial table

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

Infinite sequences

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 (simplest variant)
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: slightly more complicated construction

// matching, more complicated
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))
    }
}

Object oriented programming in Rust


Memory management

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");
}

// one object that is shared three times
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 semantic
        // because object can live longer than current thread
        thread::spawn(move || {
                owner.print();
                delay(400);
        });
    }
}

Arrays

fn main() {
    // first type of array constructor
    let array = [10, 20, 30, 40];

    // array length in runtime
    println!("array has {} items", array.len());

    // range construct + array length
    for i in 0..array.len() {
        println!("item #{} = {}", i + 1, array[i]);
    }

    // for-each loop over array
    for i in array.iter() {
        println!("{}", i);
    }
}
fn main() {
    // second type of array constructor
    let array = [1; 10];

    // array length in runtime
    println!("array has {} items", array.len());

    // range construct + array length
    for i in 0..array.len() {
        println!("item #{} = {}", i + 1, array[i]);
    }

    // for-each loop over array
    for i in array.iter() {
        println!("{}", i);
    }
}

Vectors

fn main() {
    // vector construction using macro
    let vector = vec![1, 2, 3, 4, 5];

    // vector length in runtime
    println!("vector has {} items", vector.len());

    // range construct + vector length
    for i in 0..vector.len() {
        println!("item #{} = {}", i + 1, vector[i]);
    }

    // for-each loop over vector
    for item in vector.iter() {
        println!("{}", item);
    }

    // this is even shorter
    for item in &vector {
        println!("{}", item);
    }
}

„Slice“ from array

fn main() {
    // array constructor
    let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // slice constructor
    let array2 = &array[2..6];

    // iteration over slice
    for i in array2.iter() {
        println!("{}", i);
    }
}
fn main() {
    // array constructor
    let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // slice constructor
    let array2 = &array[5..];

    // iteration over slice
    for i in array2.iter() {
        println!("{}", i);
    }
}

„Slice“ from vector

fn main() {
    // vector constructor
    let vector = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    println!("vector has {} items", vector.len());

    // slice constructor
    let slice = &vector[3..7];
    println!("slice has {} items", slice.len());
}

Threads

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");
}

Testing

A classic test pyramid

Unit tests in Rust programming language

#[test]
fn ok_test() {
}

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

Other tests in Rust


Package manager (Cargo)

images/cargo.png

Cargo operations


Selected useful packages

Some statistic for this week


Deployment


Interface to Python


Documentation