Understanding Rust

Or: How I Learned to Stop Worrying and Love the Borrow Checker

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Who Am I?

https://haltcondition.net

@tarka.haltcondition.net

Getting out of trouble by Understanding Git

Continuous Deployment for a Billion Dollar Order System

xcp: experimental accelerated & pluggable unix copy

@tarkasteve@hachyderm.io

Halt Condition consulting

Git and effective workflows

Continuous Integration & Deployment

Rust Adoption

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

How I Learned to Stop Worrying and Love the Borrow Checker

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Languages that change us

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Example

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

fn mult_mut_example() {
    let mut x = 1;
    let mx1 = &mut x;
    // Not allowed
    let mx2 = &mut x;

    *mx1 = 2;
    println!("X = {}", mx2);
}
fn mut_immut_example() {
    let mut x = 1;
    let mx1 = &mut x;
    // Still not allowed
    let rox = &x;

    *mx1 = 2;
    println!("X = {}", rox);
 }

Recap: Mutability Rules

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

fn mult_mut_example() {
    let mut x = 1;
    let mx1 = &mut x;
	let mx2 = &mut x;

    *mx1 = 2;
    println!("X = {}", mx2);
}
| let mx1 = &mut x;                                                                         ▐
|           ------ first mutable borrow occurs here
| let mx2 = &mut x;
|           ^^^^^^ second mutable borrow occurs here

Recap: Mutability Rules

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

fn mut_immut_example() {
    let mut x = 1;
	let rox = &x;
    let mx1 = &mut x;

    *mx1 = 2;
    println!("X = {}", rox);
 }
| let rox = &x;
|           ^^ immutable borrow occurs here
| let mx1 = &mut x;
|           ------ mutable borrow occurs here
|
| *mx1 = 2;
| -------- mutable borrow later used here

Recap: Move & Borrow

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

fn my_fn() {
    let person = get_person();
    save_person(person);
    println!("Name = {}", person.name);
}
fn save_person(person: Person) {
    DB::write(person);
}
| let person = Person { name: "test" };
|     ------ move occurs because `person` has type `Person`,
|            which does not implement the `Copy` trait
|
| save_person(person);
|             ------ value moved here
| println!("Name is {}", person.name);
|                        ^^^^^^^^^^^ value borrowed here after move

Recap: Move & Borrow

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

fn my_fn() {
    let person = get_person();
    let person2 = save_person(person);
    println!("Name = {}", person2.name);
}
fn save_person(person: Person) {
    DB::write(person);
    return person;
}

Recap: Move & Borrow

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

fn my_fn() {
    let person = get_person();
    save_person(&person);
    println!("Name = {}", person.name);
}
fn save_person(person: &Person) {
    DB::write(person);
}

Garbage Collection

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

All languages are Garbage Collected

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

All languages are Garbage Collected

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

All languages are Garbage Collected

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

void func() {
    int myNumbers[] =
        {1, 2, 3, ...};
   	int num = 512;

	int *ptr1 = 
    	(int*)malloc(n * sizeof(int));

	int *ptr2 = 
    	(int*)malloc(n / 2 * sizeof(int));
    
	// Some stuff with ptrs

	free(ptr1);
}

The Borrow Checker is GC

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

fn my_fn() {
    let person = get_person();
    save_person(person);
    println!("Name = {}", person.name);
}
fn save_person(person: Person) {
    DB::write(person);
}

The Compiler is GC

free(person);

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

The Compiler is Resource Management

pub trait Drop {
    fn drop(&mut self);
}

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

The Compiler is Resource Management

pub trait Drop {
    fn drop(&mut self);
}
struct File {
	fd: u64;
}

impl File {
	fn open(path: String) -> File {
    	let fd = libc::open(path);
        File { fd }
    }
}
impl Drop for File {
	fn drop(&self) {
    	libc::close(self.fd);
    }
}

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

The Compiler is Resource Management

pub trait Drop {
    fn drop(&mut self);
}
impl Drop for File {
	fn drop(&self) {
    	libc::close(self.fd);
    }
}
fn sum_file(path: String) -> u64 {
	let file = File::open(path);

	let sum = file.iter().sum();
	sum
} 

🚫 libc::close(self.fd);

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

The Compiler is Resource Management

fn do_lots_of_stuff() -> bool {

	let data = generate_data();

    {
    	let file = File::open(path);
        file.write(data.raw());
    } // 🚫 File closed here
    
	println!("SHA256 of data is {}", data.sha256());
}

'file

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

The Compiler is Resource Management

fn do_lots_of_stuff() -> bool {

	let data = {
    	let file = File::open(path);
        file.read_all()
    }; // 🚫 File closed here
    
	println!("SHA256 of data is {}", data.sha256());
}

'file

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

The Compiler is Resource Management

fn do_lots_of_stuff() -> bool {

	let data = {
    	let file = File::open(path);
        file.read_all()
    }; // 🚫 File closed here
    
	println!("SHA256 of data is {}",
 			 data.sha256());
}
fn do_lots_of_stuff() -> bool {

	let file = File::open(path);
	let data = file.read_all();

	drop(file); // 🚫 File closed
    
	println!("SHA256 of data is {}",
 			 data.sha256());
}

'file

'file

Stack & Heap

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Stack & Heap

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net


void func() {
    int myNumbers[] =
        {1, 2, 3, ...};
    
    
    
	int num = 512;


	int *ptr1 = 
    	(int*)malloc(n * sizeof(int));

	int *ptr2 = 
    	(int*)malloc(n / 2 * sizeof(int));
    
	// Some stuff with ptrs

	free(ptr1);
}

Stack

Heap

Stack & Heap

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

array

integer

ptr1

ptr2

data

data


void func() {
    int myNumbers[] =
        {1, 2, 3, ...};
    
    
    
	int num = 512;


	int *ptr1 = 
    	(int*)malloc(n * sizeof(int));

	int *ptr2 = 
    	(int*)malloc(n / 2 * sizeof(int));
    
	// Some stuff with ptrs

	free(ptr1);
}

Stack

Heap

Stack & Heap

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

array

integer

ptr1

ptr2

data


void func() {
    int myNumbers[] =
        {1, 2, 3, ...};
    
    
    
	int num = 512;


	int *ptr1 = 
    	(int*)malloc(n * sizeof(int));

	int *ptr2 = 
    	(int*)malloc(n / 2 * sizeof(int));
    
	// Some stuff with ptrs

	free(ptr1);
}

Stack

Heap

Stack & Heap

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

data

Stack

Heap

Heap in Rust

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

fn do_boxed() {
	let array = 
        [1, 2, 3, 4, ...];


	let boxed =
    	Box::new(array);


	println!("Box size is {}", 
    	     boxed.len());
             
}

array

Stack

Heap

Heap in Rust

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

fn do_boxed() {
	let array = 
        [1, 2, 3, 4, ...];


	let boxed =
    	Box::new(array);


	println!("Box size is {}", 
    	     boxed.len());
             
}

array

boxed

Stack

Heap

Heap in Rust

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

fn do_boxed() {
	let array = 
        [1, 2, 3, 4, ...];


	let boxed =
    	Box::new(array);


	println!("Box size is {}", 
    	     boxed.len());
             
}

Stack

Heap

'boxed

Heap in Rust

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

impl<T> Box<T> {
    /// Allocates memory on the heap and then places `x` into it.
    // ...
    pub fn new(x: T) -> Self {
        #[rustc_box]
        Box::new(x)
    }
}

impl Drop for Box<T> {
    fn drop(&mut self) {
    	self.ptr.deallocate();
    }
}

Heap GC in Rust

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

impl<T> Box<T> {
    /// Allocates memory on the heap
    /// and then places `x` into it.
    pub fn new(x: T) -> Self {
        #[rustc_box]
        // Compiler inserts malloc()
        // magic here.
    }
}

impl Drop for Box<T> {
    fn drop(&mut self) {
    	self.ptr.deallocate();
    }
}
fn my_fn() {
	let data = ...;
    let boxed = Box::new(data);
    
    // Do some stuff

}

🚫 deallocate();

Box in std

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Vec<> and vec![]

String

Rc<>

Arc<>

Sharing

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Sharing & Lifetimes

struct First<'a> {
    image: &'a Vec<u8>,
    // Other stuff
}

struct Second<'b> {
    image: &'b Vec<u8>,
    // Different stuff
}
fn main() {
	let image = load_image();

	let first = First {
		image: &image
	};
	let second = Second {
		image: &image
	};
    
   	process_first(first);
  	process_second(second);
}

'a

'image

'b

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Rc<>


let array = [1, 2, 3, 4, ...];









array

Stack

Heap

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Rc<>


let array = [1, 2, 3, 4, ...];


let rc = Rc::new(array);






array

rc

Stack

Heap

count: 1

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Rc<>


let array = [1, 2, 3, 4, ...];


let rc = Rc::new(array);


let rc2 = rc.clone();



rc

rc2

Stack

Heap

array

count: 2

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Rc<>


let array = [1, 2, 3, 4, ...];


let rc = Rc::new(array);


let rc2 = rc.clone();

drop(rc);

rc2

Stack

Heap

array

count: 1

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Rc<>


let array = [1, 2, 3, 4, ...];


let rc = Rc::new(array);


let rc2 = rc.clone();

drop(rc);
drop(rc2);

Stack

Heap

array

count: 0

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Rc<>


let array = [1, 2, 3, 4, ...];


let rc = Rc::new(array);


let rc2 = rc.clone();

drop(rc);
drop(rc2);

Stack

Heap

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Rc<> vs Lifetimes

struct First {
    image: Rc<Vec<u8>>,
}

struct Second {
    image: Rc<Vec<u8>>,
}
let image = load_image();

let img_rc = Rc::new(image);

let ms1 = First {
	image: img_rc.clone()
};
let ms2 = Second {
	image: img_rc.clone()
};

drop(img_rc);

// First & Second carry on existing

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

RefCell<>

let mut counter = 0;

let first = &mut counter;
// Compile will reject this reference
let second = &mut counter;
let counter = 0;
let rc_counter = RefCell::new(counter);

let first = &rc_counter;
let second = &rc_counter;

let fb = first.borrow_mut();
// Will panic here
let sb = second.borrow_mut();

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Anti-Pattern: Rc<RefCell<>>

struct MyStruct {
	data: Rc<RefCell<Vec<u8>>>
}

let data = load_data();

let ms = MyStruct {
	data: Rc::new(RefCell::new())
};

Threads

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Threads

use std::thread;

struct Config {
    go_fast: bool,
    avatar: Vec<u8>
}

let config = Config {
	go_fast: true,
    avatar: load_avatar(),
};

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Threads

fn main() {
	let config = Config {
		go_fast: true,
    	avatar: load_avatar(),
	};
    let ref1 = &config;

	let backend_thread = thread::spawn(move || backend(ref1) );
    
    backend_thread.join().unwrap();
}
error[E0597]: `config` does not live long enough
|
| let config = Config {
|     ------ binding `config` declared here
...
| let ref1 = &config;
|            ^^^^^^^ borrowed value does not live long enough

'config

'?

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Threads & Rc<>

let config = Config {
	go_fast: true,
    avatar: load_avatar(),
};
let rc = Rc::new(config);
let rc2 = rc.clone();

let backend_thread = thread::spawn(move || backend(rc2) );
| let backend_thread = thread::spawn(|| backend(rc) );
|                      ------------- --^^^^^^^^^^^^^^^^^^^
|                      |             |
|                      |             `Rc<Config>` cannot be sent between threads safely
|                      required by a bound introduced by this call
|
= help: the trait `std::marker::Send` is not implemented for `Rc<Config>`

Traits & Markers

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Traits as API

trait Connection {
    fn connect(&mut self);
}

struct HttpClient { host: Host }

impl Connection for HttpClient {
    fn connect(&mut self) {
    	//  Use self.host to connect
    }
}

impl io::Read for HttpClient {
    fn read(&mut self, buf: &mut [u8]) 
        -> io::Result<usize>
    {
        // ...
        Ok(nread)
    }
}
fn process_connection<C>(mut conn: C)
    where C: Connection + Read
{
    let mut buf = [0; 20];
    conn.connect();
    conn.read(&mut buf);

    // Process buf
}

fn main() {
     let host = Host { ... };
     let client = HttpClient { host };
     process_connection(client);
 }

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Traits as Constraint Marker

pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
    F: FnOnce() -> T,
    F: Send + 'static,
    T: Send + 'static,
{
    Builder::new().spawn(f).expect("failed to spawn thread")
}

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Send + Sync

pub unsafe auto trait Sync {
    // Empty
}
pub unsafe auto trait Send {
    // empty
}

Data is Send if it can be sent between threads safely.

This means No Unsafe Mutable.

Data is Sync if it can be shared between threads safely.

It promises that data is Mutable Safely.  i.e. Mutexes & Atomics.

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Send + Sync

pub unsafe auto trait Sync {
    // Empty
}
pub unsafe auto trait Send {
    // empty
}

The compiler will auto-apply Send/Sync if all fields are Send/Sync.

Data is Send if it can be sent between threads safely.

This means No Unsafe Mutable.

Data is Sync if it can be shared between threads safely.

It promises that data is Mutable Safely.  i.e. Mutexes & Atomics.

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

'static

fn get_name() -> &'static str {
	"Steve"
}
struct StaticData {
    avatar: Vec<u8>,
    uuid: String,
}

struct NotStatic<'a> {
    image: &'a Vec<u8>,
}

"A reference lifetime 'static indicates that the data pointed to by the reference lives for the remaining lifetime of the running program."

"A 'static trait bound means the type does not contain any non-static references."

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Threads and Rc

pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
    F: FnOnce() -> T,
    F: Send + 'static,
    T: Send + 'static,
{
	// ...
}

let rc = Rc::new(config);

let backend_thread = thread::spawn(|| backend(rc) );

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Threads and Rc

impl<T> !Send for Rc<T> {}

impl<T> !Sync for Rc<T> {}

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Threads and Rc

let config = Config {
	go_fast: true,
    avatar: load_avatar(),
};
let arc1 = Arc::new(config);
let arc2 = rc.clone();

let backend_thread = thread::spawn(move || backend(arc1) );
let frontend_thread = thread::spawn(move || frontend(arc2) );

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Arc is Atomic

struct ArcInner<T> {
    strong: atomic::AtomicUsize,

	weak: atomic::AtomicUsize,

    data: T,
}
unsafe impl<T: Sync + Send> Send for Arc<T> {}
unsafe impl<T: Sync + Send> Sync for Arc<T> {}

Async

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Async

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

https://emschwartz.me/async-rust-can-be-a-pleasure-to-work-with-without-send-sync-static/

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Conclusion & Links

Rustonomicon

Rust Design Patterns

Evan Schwartz

https://haltcondition.net

@tarkasteve@hachyderm.io

@tarka.haltcondition.net

Like and Subscribe!