Or: How I Learned to Stop Worrying and Love the Borrow Checker
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
https://haltcondition.net
@tarka.haltcondition.net
@tarkasteve@hachyderm.io
Git and effective workflows
Continuous Integration & Deployment
Rust Adoption
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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);
}
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
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
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
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;
}
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);
}
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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);
}
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);
}
free(person);
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
pub trait Drop {
fn drop(&mut self);
}
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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
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
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());
}
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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());
}
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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());
}
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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
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
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
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
data
Stack
Heap
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
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
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
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();
}
}
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();
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
Vec<> and vec![]
String
Rc<>
Arc<>
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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);
}
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
let array = [1, 2, 3, 4, ...];
array
Stack
Heap
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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
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
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
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
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
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
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
struct MyStruct {
data: Rc<RefCell<Vec<u8>>>
}
let data = load_data();
let ms = MyStruct {
data: Rc::new(RefCell::new())
};
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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
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
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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>`
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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
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
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
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
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
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
impl<T> !Send for Rc<T> {}
impl<T> !Sync for Rc<T> {}
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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
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> {}
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net
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
Rustonomicon
Rust Design Patterns
Evan Schwartz
https://haltcondition.net
@tarkasteve@hachyderm.io
@tarka.haltcondition.net