Neophyte Rustacean

One month with Rust

Me

  • Professional Java/JVM developer
  • Experienced in C
  • Used C++, Scala, Python, assembly, ...
  • Haskell for 18 months

What is Rust

  • Strict
  • Immutable* by default
  • Lifecycle managed
  • Low overhead

Goals of Rust

  • Memory safe
  • Zero-cost abstraction
  • No data races
  • Efficient

    Aimed at replacing C/C++

    Not aimed at being an alternative to Haskell

    I would consider it a competitor to Go

Syntax

  • Bullet One
  • Bullet Two
  • Bullet Three

Example from the book

use std::io;
fn main() {
    println!("Guess the number!");
    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .ok()
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}

Example from the book

use std::io;
fn main() {
    println!("Guess the number!");
    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .ok()
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}
  • Prelude is minimal
    • No IO
    • Mostly lifecycle and memory management
    • Option, Result, String, Vec
  • 'std' create is small too
    • controlled via RFCs

Example from the book

use std::io;
fn main() {
    println!("Guess the number!");
    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .ok()
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}
  • Defaults to returning nothing, so side-effects are no delineated

Example from the book

use std::io;
fn main() {
    println!("Guess the number!");
    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .ok()
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}
  • println! from the io module
  • {} for string interpolation
  • ! means this is a macro
    • Compiler checks format string

Example from the book

use std::io;
fn main() {
    println!("Guess the number!");
    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .ok()
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}
  • Bindings are immutable by default
  • mut makes it mutable
  • must be used when passing a mutable reference into a function too, as references are immutable by default
  • More on this later

Example from the book

use std::io;
fn main() {
    println!("Guess the number!");
    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .ok()
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}
  • String::new is a function associated with the String type
  • io::stdin is a function in the io module
  • foo.bar(...) a method call
    • sort of syntactic sugar for bar(foo, ...) with expected resolution of bar

Example from the book

use std::io;
fn main() {
    println!("Guess the number!");
    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .ok()
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}
  • Rust has Option and Result types in the prelude
  • Parameterised over their types
    • io::Result<A> is an alias for std::Result<A, Error>
  • ok is Result<A> -> Option<A>
  • expect is Option<A> ->A
    • panic! if it's None

Other syntax

and control flow

If

Works as you'd expect, no parentheses

If trailing else, expression with last value in each

If not, expression of type ()

 

if condition {

} else if {

} else {

}

For

For-each style for, using Iterator trait

 

for var in expression {

  ..

}

 

for i in 0..10 {

  // goes from zero to nine!

}

While and loop

while statement and infinite loop, both unit type

 

while condition {

  ..

}

 

loop {

  // prefer this to while "true"

}

Ownership, borrowing and references

Ownership

  • Bindings own their data for their lifetime
  • At most one owner for each piece of data
  • Deallocated when it goes out of scope
  • This is part of what prevents data races and provided memory safety
  • One of the most interesting parts of Rust

Ownership - moving

let x = vec![0,1,2]

let y = x

println!("{}", x[0]) // will fail

 

Ownership of the vector was moved to y, so x is not longer a valid binding. rustc enforces this:

 

  error: use of moved value: `x`

  note: `x` moved here because it has type

     `collections::vec::Vec<i32>`, which is moved by default

  let y = x;

Ownership - moving

fun foo(v: Vec<i32>) {..}

let x = vec![0,1,2]

foo(x);

println!("{}", x[0]) // will fail

 

This will also fail, since ownership was moved to the function (as an argument)

Ownership - Copy

Some types implement the Copy trait

indicating that they can freely be copied without introducing data races

Numeric types (no pointers)

 

let v = 1;

let v2 = v;

println!("{}", v); // works

Ownership - borrowing

What if we want to pass data to a function and then use it afterwards? The function could return it, but that would be very tedious. Instead we let the function borrow our data (implemented as a pointer).

 

fn foo(v: &Vec<i32>) {..}

let v = vec![0,1,2];

foo(v)

println!("{}", v[0]); // works

Ownership - borrowing

References are immutable by default, can't change data

 

fn foo(v: &Vec<i32>) {v.push(9);}

let v = vec![0,1,2];

foo(v)

println!("{}", v[0]);

 

error: cannot borrow immutable borrowed content `*v` as mutable
v.push(9);

Ownership - borrowing

You can use a mutable reference, on both the argument and reference

 

fn foo(v: &mut Vec<i32>) {v.push(9);}

let v = vec![0,1,2];

foo(&mut v)

println!("{}", v[0]); // works

Ownership - borrowing

You cannot have mutable and immutable references at the same time

 

fn foo(v: &Vec<i32>) {..}

let v = vec![0,1,2];

let v2 = &mut v2

foo(v); // v2 is a mutable borrow,

        //so can't pass immutable reference to function

Ownership - borrowing

If you limit the scope of the mutable borrow, it no longer exists at the point we want an immutable borrow.

 

fn foo(v: &Vec<i32>) {..}

let v = vec![0,1,2];

{

  let v2 = &mut v2

}

foo(v); // works

Ownership - rules

You can have exactly one mutable reference,

 or any number if immutable references,

  but not both

 

Traits

Typeclasses for Rust

Traits

Rust has traits, which are basically typeclasses

x

neophyte-rustacean

By doctau

neophyte-rustacean

  • 1,006