61
Nicholas Matsakis Mozilla Research

Rust tutorial from Boston Meetup 2015-07-22

Embed Size (px)

Citation preview

Nicholas Matsakis!Mozilla Research

So, you want more control?

C++?

OMG!

2

Too slow!

Other things you might want:! • Standalone library, as you would get from C • Interface with another runtime, e.g. Ruby   • Use a really cool language :)

My god, it’s full of bugs

3

Dangling pointers !Segmentation faults !Double frees !Uninitialized data !Null pointer exceptions !Resource leaks (DB handle) !Data races

Solved by GC

Not so much.

4

Systems programming without the hasslecrashes!heisenbugs!fear

Parallel!

// sums all the positive values in `v` fn sum_pos(v: &Vec<i32>) -> i32 { let mut sum = 0; for i in v.iter().filter(|i| **i > 0) { sum += *i; } sum }

High-level coding

5

Iterators.

Closures.

Assembly code

6

leaq (%rdi,%rsi,4), %rcx xorl %eax, %eax jmp .LBB5_1 .LBB5_3: addl %edx, %eax .align 16, 0x90 .LBB5_1: cmpq %rdi, %rcx je .LBB5_4 movl (%rdi), %edx addq $4, %rdi testl %edx, %edx jle .LBB5_1 jmp .LBB5_3 .LBB5_4: retq

fn foo(v: &Vec<i32>) -> i32 { v.iter() .filter(|i| **i > 0) .map(|i| *i) .sum() }

Higher-level coding

7

…generates the same assembly code.

Safe

8

fn this_wont_compile(v: &mut Vec<i32>) -> i32 { let mut sum = 0; for &i in v.iter() { sum += i; if i > 0 { v.push(0); } } sum }

error: cannot borrow `*v` as mutable because it is also borrowed as immutable if i > 0 { v.push(0); } ^ note: previous borrow of `*v` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `*v` until the borrow ends for &i in v.iter() { ^

Might free underlying buffer.

Parallel

9

use std::thread; fn qsort(data: &mut [i32]) if data.len() <= 1 { return; } let mid = partition(data[0], data); let (left, right) = data.split_at_mut(mid); let t1 = thread::scoped(|| qsort(left)); qsort(right); }

Sort left and right in parallel.Caveat: unstable API.

Open and welcoming

Rust has been open source from the beginning. !

Open governance model based on public RFCs. !

We have an active, amazing community.

10

Getting Started

11

You can either install Rust, or just use play.rust-lang.org

Exercises are available at: !

http://nikomatsakis.github.io/rust-tutorial-boston-20150722/

Outline

12

1. The big ideas:!a. Ownership b. Borrowing

2. Everyday life:!a. Data types b. Modules and privacy c. Cargo

Ownership!!n. The act, state, or right of possessing something.

13

Borrow!!v. To receive something with the promise of returning it.

The Big Idea

Ownership and borrowing:!!1. All memory has a clear owner. 2. Others can borrow from the owner. 3. Owner cannot free or mutate the

memory while it is borrowed.

14

Ownership/Borrowing

Memory safety

Data-race freedom

No need for a runtime

GCC++

15

Clean up the mess

16

http://mylifeandkids.com/messy-house-edition-boys-bedroom-2/

Ownership17

fn give() { let mut vec = vec![]; vec.push(1); vec.push(2); take(vec); … }

fn take(vec: Vec<i32>) { // … } !!!

Ownership

Take ownership of a Vec<i32>

18

fn give() { let mut vec = Vec::new(); vec.push(1); vec.push(2); take(vec); … } vec.push(2);

Compiler enforces moves

fn take(vec: Vec<i32>) { // … } !!!Error: vec has been moved

Prevents: - use after free - double moves - …

19

void give() { Vector vec = …; vec.add(1); vec.add(2); take(vec); vec.add(3); }

void take(Vector vec) { // … } !!!

“Ownership” in Java

Take reference to Vector

20

Mutability

21

fn prefix_sum(mut v: Vec<i32>) -> Vec<i32> { let mut sum = 0; for i in 0 .. v.len() { sum += v[i]; v[i] = sum; } v }

http://is.gd/wCtQQZ

1, 2, 3, 4 1, 3, 6, 10

(caller) (prefix sum)

Clone

22

fn main() { let d = vec![1, 3, 4, 10]; let ps = prefix_sum(d); println!("prefix sum of {:?} is {:?}", d, ps); }

http://is.gd/nbuxdV

Clone

23

fn main() { let d = vec![1, 3, 4, 10]; let ps = prefix_sum(d.clone()); println!("prefix sum of {:?} is {:?}", d, ps); }

http://is.gd/nbuxdV

1, 2, 3, 4 1, 3, 6, 10(caller) (prefix sum)

1, 2, 3, 4

24

struct Point { x: u32, y: u32 } !fn area(ul: Point, lr: Point) -> u32 { (lr.x - ul.x) * (lr.y - ul.y) } !fn main() { let origin = Point { x: 0, y: 0 }; let unit = Point { x: 1, y: 1 }; let here = Point { x: 5, y: 6 }; println!(“{:?}”, area(origin, unit)); println!(“{:?}”, area(origin, here)); }

Declare a struct type Point with two

fields, x and y.

// 1// ?

http://is.gd/5dDnaH

32-bit unsigned integer

“Copy” types

25

#[derive(Copy, Clone)] struct Point { x: u32, y: u32 }

26

Default: Type cannot be copied. Values move from place to place. Example: File descriptor. !

Clone: Type is expensive to copy, so make it explicit by calling clone(). Example: Vector, hashtable.!!

Copy: Type is implicitly copied whenever it is referenced. Example: u32, i32, Point

27

Exercise #1.

* Actually: mutation only in controlled circumstances

*

Shared borrow (&T)

Sharing Mutation

Mutable borrow (&mut T)29

Sharing Mutation

30

fn sum(v: Vec<i32>) -> i32 { let mut s = 0; for i in 0 .. v.len() { s += v[i]; } s } !fn main() { let v = vec![1, 2, 3]; println!(“{:?}”, sum(v)); }

Take ownership of a Vec<i32>

Give ownership

31

fn sum(v: &Vec<i32>) -> i32 { let mut s = 0; for i in 0 .. v.len() { s += v[i]; } s } !fn main() { let v = vec![1, 2, 3]; println!(“{:?}”, sum(&v)); }

Borrow!Vec<i32>

Lend the vectorhttp://is.gd/aHalet

32

fn prefix_sum(v: &mut Vec<i32>) { let mut s = 0; for i in 0 .. v.len() { s += v[i]; v[i] = s; } } !fn main() { let mut v = vec![1, 2, 3]; prefix_sum(&mut v); println!("{:?}", v); }

Mutable borrow

Mutable loan

http://is.gd/jvKmF2

fn example() { let mut names = Vec::new(); names.push(..); names.push(..); let name = &names[1]; names.push(..); print(name); }

names

data

length

capacity

“brson”

“pcwalton”

name

“brson”

“pcwalton”

“acrichto”

Sharing: more than one pointer to same memory.

Dangling pointer: pointer to freed memory.

Mutating the vector freed old contents.

33

Rust solution

34

Compile-time read-write-lock:!!Creating a shared reference to X “read locks” X. - Other readers OK. - No writers. - Lock lasts until reference goes out of scope. !Creating a mutable reference to X “writes locks” X. - No other readers or writers. - Lock lasts until reference goes out of scope.

Never have a reader/writer at same time.

fn example() { let mut names = Vec::new(); names.push(“brson”); names.push(“pcwalton”); let name = &names[1]; names.push(“acrichto”); println!(“{:?}”, name); }

Borrow “locks” `names` until `name` goes out of scopeError: cannot mutate

`names` while borrowed

35http://is.gd/jeKW1E

Outside of borrow scope — OK.

Scope of borrow in this case covers only the loop body.

36http://is.gd/thMY5N

fn main() { let mut names = Vec::new(); names.push("brson"); names.push("pcwalton"); for i in 0 .. names.len() { let name = &names[i]; names.push("acrichto"); println!("{:?}", name); } names.push("acrichto"); }

Rust reasons about scopes

37

fn main() { let mut names = Vec::new(); names.push("brson"); names.push("pcwalton"); for i in 0 .. names.len() { let name = &names[i]; println!("{:?}", name); names.push("acrichto"); } names.push("acrichto"); }

Even though reference is not used, it is still in scope for the entire block..

http://is.gd/pLE8bb

Take a break.

38

Daily life in Rust

39

Methods

40

struct Point { x: f32, y: f32, } !impl Point { fn new() -> Point { Point { x: 0.0, y: 0.0 } } ! fn negate(&self) -> Point { Point { x: -self.x, y: -self.y } } }

http://is.gd/KbbORT

Common derivations

41

#[derive(PartialEq)]

+ #[derive(PartialOrd)]

#[derive(Clone)]

#[derive(Debug)]

x == y, x != y

x < y, x <= y, …

x.clone()

+ #[derive(Copy)] use(x); use(x);

println!(“{:?}”, x);

#[derive(Hash)] HashMap<T>

Enums

42

struct Point {..} !enum Shape { Circle { origin: Point, radius: f32 }, ! Rectangle { ul: Point, lr: Point } }

43

struct Point {..} !enum Shape { Circle { origin: Point, radius: f32 }, Rectangle { ul: Point, lr: Point } } !impl Shape { fn unit_circle() -> Shape { Shape::Circle { origin: Point { x: 0.0, y: 0.0 }, radius: 1.0 } } }

const PI: f32 = 3.14159; impl Shape { fn area(&self) -> f32 { match *self { Shape::Circle { origin: _, radius: r } => PI * r * r, ! Shape::Rectangle { ul, lr } => (lr.y - ul.y).abs() * (lr.x - ul.x).abs() } } }

44http://is.gd/a2YcvG

Option

45

enum Option<T> { None, Some(T), }

No null types

46

class Shape { Color color; ! Shape() { } ! Color getColor(Color default) { if (color != null) return color; return default; } }

(Java)

47

struct Shape { color: Option<Color> } !impl Shape { fn new() -> Shape { Shape { color: None } } ! fn get_color(&self, default: Color) -> Color { match self.color { None => default, Some(ref c) => c.clone() } } } (Rust)

48

match self.color { None => default, Some(ref c) => c.clone() }

if let Some(ref c) = self.color { c.clone() } else { default }

self.color.unwrap_or(default)

self.color.unwrap_or_else(|| default)

Slices

49

fn main() { // Heap-allocated. let v: Vec<i32> = vec![1, 2, 3, 4]; ! // Reference to one element. let e: &i32 = &v[1]; ! // Reference to many elements. let slice: &[i32] = &v[1..3]; }

http://is.gd/QftPT8

Mutable slices

50

fn main() { // Heap-allocated. let mut v: Vec<i32> = vec![1, 2, 3, 4]; println!(“v={:?}”, v); { let slice = &mut v[..]; slice[1] += 22; } println!(“v={:?}”, v); }

http://is.gd/31rKv5

For loops and slices

51

for x in &v { // x is an &i32 }

let v: Vec<i32> = vec![1, 2, 3, 4];

for x in &mut v { // x is an &mut i32 }

for x in v { // x is an i32 } // v is consumed after loop

for converts its argument into an iterator using the IntoIterator trait

Iterators

52

struct PlayerScore { player_name: String, score: u32 } !fn high_scorers(v: Vec<PlayerScore>) -> Vec<(String, u32)> { v.into_iter() .filter(|ps| ps.score > 20) .map(|ps| (ps.player_name, ps.score)) .collect() }

Programming in the large

53

Cargo

54

> cargo new my_project > cargo new —-bin my_project

> cd my_project > emacs

Create a template for a new project:

Edit your project:

> cargo build [—-release] > cargo test

Build and test your project:

http://doc.crates.io/guide.html

Dependencies

55

[package] name = "hello_world" version = "0.1.0" authors = ["Your Name <[email protected]>”] ![dependencies] regex = "0.1.41"

Cargo.toml

lib.rs

extern crate regex;

Modules

56

mod data; mod code;

lib.rs/main.rs

data/mod.rs

mod point; mod shape;

data/point.rs

struct Point { }

:: data point shape code

data/shape/mod.rs

struct Point { }

Often used to make a mod test for unit tests, or for demonstations.

Inline modules

57

mod data { mod point { .. } ! mod shape { .. } } !mod code {

lib.rs/main.rs Exactly the same as creating a separate file.

Use

58

:: data point shape code

code.rs

use data::point::Point;

data/mod.rs

use data::point::Point; use self::point::Point;

data/shape.rs

use data::point::Point; use super::point::Point;

Privacy

59

Privacy is the default, use pub to override.

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

impl Point { pub fn m(&self); }

pub enum Shape { … }

pub mod child;

Private means: code in this module or a descendant.

Where to learn more

60

doc.rust-lang.org/book

users.rust-lang.org / IRC / Stackoverflow

doc.rust-lang.org/std

61

Thanks for

listenin

g!