Rust

Johan Burell

- an introduction

don't we have enough languages?

what is rust?

Rust is a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety.

Shared

Mutable

Concurrent

Choose two!

Memory safety

Intended audience?

Real time computing

"C-developer"

Linus Thorvalds

"Ruby-developer"

Steve Klabnik

Many older languages [are] better than new ones. We keep forgetting already-learned lessons.

[...]

[Rust is] Technology from the past come to save the future from itself

- Graydon Hoare, Creator of Rust at Mozilla

Features

  • guaranteed memory safety
  • move semantics
  • type inference
  • zero-cost abstractions
  • algebraic data types
  • pattern matching
  • trait-based generics
  • threads without data races
  • macros
  • minimal runtime
  • efficient C bindings
  • ...and more!

Hello world

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

Guaranteed memory safety

what does 'memory allocation' mean?

Allocation from whom?

How?

RAII

(also known as the worlds worst acronym)

Memory Safety

fn main() {
    let a:u32;
    println!("Value: {}", a); // a?
}
error[E0381]: use of possibly uninitialized variable: `a`
 --> examples/ex1a.rs:3:27
  |
3 |     println!("Value: {}", a);
  |                           ^ use of possibly uninitialized `a`
fn main() {
    let a:u32 = 1;
    println!("Value: {}", a);
}
Example #1a
Example #1b

Memory Safety

fn main() {
    let a:u32 = 1;
    a = 2;
    println!("Value: {}", a);
}
error[E0384]: re-assignment of immutable variable `a`
 --> examples/ex1c.rs:3:5
  |
2 |     let a:u32 = 1;
  |         - first assignment to `a`
3 |     a = 2;
  |     ^^^^^ re-assignment of immutable variable
fn main() {
    let mut a:u32 = 1;
    a = 2;
    println!("Value: {}", a);
}
Example #1c
warning: value assigned to `a` is never read, #[warn(unused_assignments)] on by default
 --> examples/ex1d.rs:2:9
  |
2 |     let mut a:u32 = 1;
  |   
Example #1d

Memory Safety

fn create_value_ref<'a>() -> &'a u32 {
    let a:u32 = 2;
    return &a; // Not allowed!
}

fn create_value_move() -> u32 {
    let a:u32 = 3;
    return a;
}

fn main() {
    let a:&u32 = create_value_ref();
    println!("Value: {}", *a);

    let b:u32 = create_value_move();
    println!("Value: {}", b); 
}
error: `a` does not live long enough
 --> examples/ex1e.rs:3:13
  |
3 |     return &a; // Not allowed!
  |             ^ does not live long enough
4 | }
  | - borrowed value only lives until here
Example #1e

Memory Safety

#![feature(box_syntax)]
fn create_value() -> Box<u32> {
    let a:Box<u32> = box 2;
    return a;
}

fn main() { 
    let a:u32 = *create_value();
    println!("Value: {}", a);
}
Example #1f
Running `target/debug/examples/ex1f`
Value: 2

Move semantics

Data can be:

  • Moved

  • Cloned

  • Borrowed

move semantics

#[derive(Debug)]                struct A;
#[derive(Debug, Clone)]         struct B;
#[derive(Debug, Clone, Copy)]   struct C;

fn move_to<T>(v: T) {
    let i_have_stolen_the_v = v;
}

fn main() {
    let a = A; let b = B; let c = C; let d = 3;
    move_to(a);
    println!("a: {:?}", a);  //<-- can't do this!
    move_to(b.clone());  
    println!("b: {:?}", b);
    move_to(c);           
    println!("c: {:?}", c);
    move_to(d);
    println!("d: {:?}", d);
}
Example #2a
error[E0382]: use of moved value: `a`
  --> examples/ex2a.rs:12:25
   |
11 |     move_to(a); 
   |             - value moved here
12 |     println!("a: {:?}", a);  //<-- can't do this!
   |                         ^ value used here after move
   |
   = note: move occurs because `a` has type `A`, which does not implement the `Copy` trait

move semantics

#[derive(Debug)]
struct A { b: B }
#[derive(Debug, Clone)]
struct B { c: i32 }
 
fn you_can_borrow_this(a: &A) {
    let mut x = (*a).b.clone();
    x = B{ c: x.c * 2 };
    println!("x: {:?}", x);
}
    
fn main() {
    let y = A{ b: B{ c: 3 } };
    you_can_borrow_this(&y);
    println!("y: {:?}", y);
}
Example #2b
Running `target/debug/examples/ex2b`
x: B { c: 6 }
y: A { b: B { c: 3 } }

Type Inference

Type inference

fn main() {
    let a;
    let b = 2;
    a = 3;
    println!("Result: {} + {} + 4 = {}", a, b, a + b + 4);
}
Example #3a
Running `target/debug/examples/ex3a`
Result: 3 + 2 + 4 = 9

Type inference

Example #3b
Running `target/debug/examples/ex3b`
Type is: i32
#![feature(core_intrinsics)]
fn print_type_of<T>(_: &T) {
    println!("Type is: {}", unsafe { 
        std::intrinsics::type_name::<T>()
    }); 
}

fn main() {
    let a = 1;
    print_type_of(&a);
}   

Type inference

Example #3c
Running `target/debug/examples/ex3c`
Type is: u8
#![feature(core_intrinsics)]
fn print_type_of<T>(_: &T) {
    println!("Type is: {}", unsafe { 
        std::intrinsics::type_name::<T>()
    }); 
}

fn main() {
    let a = 1;
    let b = 3u8;
    let c = a + b;
    print_type_of(&c);
}   

Type inference

Example #3d
error[E0308]: mismatched types
  --> examples/ex3d.rs:11:17
   |
11 |     let c = a + b;
   |                 ^ expected i8, found u8

error[E0277]: the trait bound `i8: std::ops::Add<u8>` is not satisfied
  --> examples/ex3d.rs:11:13
   |
11 |     let c = a + b;
   |             ^^^^^ the trait `std::ops::Add<u8>` is not implemented for `i8`
// ...

fn main() {
    let a:i8 = -1;
    let b = 3u8;
    let c = a + b;
    print_type_of(&c);
}

Type inference

Example #3e
Running `target/debug/examples/ex3e`
Type is: i8
// ...

fn main() {
    let a:i8 = -1;
    let b = 3u8;
    let c = a + b as i8;
    print_type_of(&c);
}

Algebraic data types

Product Types

Sum Types

  • Structs

  • Tuples

  • enum

Algebraic data types

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

fn main() {
    let p = Point {x: 3.0, y: 5.2};
    println!("Value: {:?}", p);
}   
Example #4a
Running `target/debug/examples/ex4a`
Value: Point { x: 3, y: 5.2 }

Algebraic data types

struct Point {
    x:f32,
    y:f32,
}

struct NotAPoint {}

#[derive(Debug)]
enum ReturnVal { Point, NotAPoint }

fn main() {
    let p = ReturnVal::NotAPoint;
    println!("Value: {:?}", p);
}
Example #4b
Running `target/debug/examples/ex4b`
Value: NotAPoint

Algebraic data types

#[derive(Debug, Clone)]
enum ReturnVal { Point{ x:f32, y:f32 }, NotAPoint }

fn get_val(input:f32) -> ReturnVal {
    if input > 1.0 {
        ReturnVal::Point{ x: input, y: input} 
    } else { 
        ReturnVal::NotAPoint 
    }
}

fn main() {
    let p = get_val(1.5);
    println!("Value: {:?}", p);
}
Example #4c
Running `target/debug/examples/ex4c`
Value: Point { x: 1.5, y: 1.5 }

Algebraic data types

use std::env;

fn main() {
    let mut args = env::args();
    let exe = args.next(); // Name of app
    let arg1 = args.next(); // Can it be NULL?!??
    println!("{:?}\n{:?}", exe, arg1);
}   
Example #4d
Running `target/debug/examples/ex4d`
Some("target/debug/examples/ex4d")
None

Algebraic data types

use std::fs::File;

fn get_file(filename: &str) -> Result<File, std::io::Error> {
    //let f = try!(File::open(filename));
    let f = File::open(filename)?;
    Ok(f)
}

fn main() {
    match get_file("examples/ex1a.rs") {
        Ok(f) => println!("{:?}", f),
        Err(e) => println!("OOPS! {}", e),
    }
}
Example #4e
Running `target/debug/examples/ex4e`
File { fd: 3, path: "../examples/ex1a.rs", read: true, write: false }
Running `target/debug/examples/ex4e`
OOPS! No such file or directory (os error 2)

Algebraic data types

fn plus_two(v:i32) -> Result<i32, String> { Ok(v + 2) }
fn one_div_by(v:i32) -> Result<i32, String> { 
    if v == 0 { 
        Err("Div by 0".to_string())
    } else { 
        Ok(1 / v) 
    } 
} 
fn times_three(v:i32) -> Result<i32, String> { Ok(v * 3) } 

fn main() {
    let a = Ok(-2)
        .and_then(plus_two)
        .and_then(one_div_by)
        .and_then(times_three);
    println!("{:?}", a);
}
Example #4f
Running `target/debug/examples/ex4f`
Err("Div by 0")

pattern matching

pattern matching

struct Coord {
    x:f32,
    y:f32,
    z:f32,
}

fn main() {
    let c = Coord{ x: 5.0, y: 4.0, z: 3.0 };
    let c2 = (42, 52);
    let Coord { x, z, .. } = c;
    let (x2, y2) = c2;

    println!("Struct x: {}, z: {}", x, z);
    println!("Tuple  x2: {}, y2: {}", x2, y2);
}
Example #5a
Running `target/debug/examples/ex5a`
Struct x: 5, z: 3
Tuple  x2: 42, y2: 52

pattern matching

fn test_pattern(v: i32) -> () {
    match v {
        a if a > 4 => println!("5 or bigger!"),
        1 ... 2    => println!("1..2"),
        3 | 4      => println!("3|4"),
        _          => println!("Don't know!"),
    }
}   
    
fn main() {
    let _:Vec<()> = (0..)
        .take(6)
        .map(test_pattern)
        .collect();
}
Example #5b
Running `target/debug/examples/ex5b`
Don't know!
1..2
1..2
3|4
3|4
5 or bigger!

Types and traits

types and traits

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

impl Point {
    fn at_x5_and_y10() -> Point {
        Point{x:5.0, y:10.0}
    }   
}

trait Double {
    fn double(&self) -> Self;
}

impl Double for Point {
    fn double(&self) -> Self {
        Point{ x: self.x * 2.0, y: self.y * 2.0}
    }   
}

fn main() {
    let p = Point::at_x5_and_y10();
    println!("Coordinates: {:?}, {:?}", p, p.double());
}
Example #6a
Running `target/debug/examples/ex6a`
Coordinates: Point { x: 5, y: 10 }, Point { x: 10, y: 20 }

types and traits

use std::convert::From;
use std::ops::Add;

#[derive(Debug)]
struct Celsius(f32);
struct Farenheit(f32);
    
impl From<Farenheit> for Celsius {
    fn from(f:Farenheit) -> Self { Celsius((f.0 - 32.0) * (5.0 / 9.0)) }
}

impl Add for Celsius {
    type Output = Celsius;
    fn add(self, rhs:Celsius) -> Self { Celsius(self.0 + rhs.0) }
}

fn main() { 
    let c = Celsius(37.0);
    let f = Farenheit(95.3);
    println!("Temp sum: {:?}", c + Celsius::from(f));
}
Example #6b
Running `target/debug/examples/ex6b`
Temp sum: Celsius(72.16667)

Functional style

functional style

fn main() {
    let mut after_filter = vec!();
    let mut after_mul = vec!();
    let result:i32 = (0..10)
        .filter(|v| v % 2 == 0)
        .map(|v| { after_filter.push(v); v } ) 
        .map(|v| v * 3)
        .map(|v| { after_mul.push(v); v } ) 
        .sum();

    println!("The result is: {}, filter: {:?}, mul: {:?}",
             result,
             after_filter,
             after_mul);
}
Example #7a
Running `target/debug/examples/ex7a`
The result is: 60, filter: [0, 2, 4, 6, 8], mul: [0, 6, 12, 18, 24]

questions?

Further reading:

Slides:                                       https://slides.com/burre83/rust_intro

Code:                        https://github.com/jburell/rust_presentation

into_rust():                                                                    http://intorust.com/

AreWeWebYet?:                                            http://arewewebyet.org/

AreWeIDEYet?:                                             https://areweideyet.com/

TWIR:                                                         https://this-week-in-rust.org/

RustByExample:                                        http://rustbyexample.com/

AwesomeRust:        https://github.com/kud1ing/awesome-rust

Made with Slides.com