rust-lang
TRANSCRIPT
Rust
Anthony Broad-CrawfordCTO @ Fooda
Rust is a systems programming language that runs blazingly fast, prevents almost all crashes*, and eliminates data races.
rust
1. guaranteed memory safety2. threads without dataraces3. zero-cost abstractions (done at compile time)4. trait-based generics5. pattern matching6. type inference7. & more
Cargo is a tool that allows Rust projects to declare their various dependencies, and ensure that you'll always get a repeatable build.
and tests ...
cargo
1. introduces conventions over configuration2. declarative syntax for dependencies3. declarative syntax for compilation profiles4. easy execution of unit, integration, & benchmark
tests
our obligatory hello world
fn main() { println!("Hello, world!");}
//> Cargo run//> Hello, world!//
let's go create a hello world with Cargo!
Using cargo to create projects
Rust's package manager
Usage: cargo <command> [<args>...] cargo [options]
Options: -h, --help Display this message -V, --version Print version info and exit --list List installed commands -v, --verbose Use verbose output
Some common cargo commands are: build Compile the current project clean Remove the target directory doc Build this project's and its dependencies' documentation new Create a new cargo project run Build and execute src/main.rs test Run the tests bench Run the benchmarks update Update dependencies listed in Cargo.lock
Primative types, Memory safety & zero cost abstractions
primatives
fn main() { //integers let i: i8 = 1; // i16, i32, i64, and i are available //unsigned let u: u8 = 2; // u16, u32, u64, and u are available //floats let f: f32 = 1.0; // f64 also available //booleans let b: bool = true; // false also available, duh //string and characters let c: char = 'a'; let s: &str = "hello world";}
variable bindings
fn main() {
let x: int = 1; //explicitly declare type let y = 2i; //type inference let (a,b,c) = (1i,2i,3i); //variable declaration via patterns let a = [1, 2, 3]; //array literals let s = "hello"; //string literal
println!("*_^ x = {}, y = {}, a,b,c = {},{},{}", x,y,a,b,c);}
//> Cargo run//> *_^ x = 1, y = 2, a,b,c = 1,2,3//
variable mutability
fn main() {
let x: int = 1; x = 10;
println!("The value of x is {}", x);}
//Cargo run//error: re-assignment of immutable variable `x`// x = 10;// ^~~~~~~
variable mutability
fn main() {
let mut x: int = 1; x = 10;
println!("The value of x is {}", x);}
//Cargo run//warning: value assigned to `x` is never read// let mut x: int = 1;// ^~~~~~~
The Rust compiler is SUPER helpful
fn main() {
let mut x: int = 1; x = 10;
println!("The value of x is {}", x);}
//Cargo run//warning: value assigned to `x` is never read// let mut x: int = 1;// ^~~~~~~
stack vs. heap
fn main() {
let y: int = 1; //allocated on the stack let x: Box<int> = Box::new(10); //allocated on the heap
println!("Heap {}, Stack {}", x, y);}
//> Cargo run//> Heap 10, Stack 1//
heap allocation creates pointers
fn main() {
let x: Box<int> = box 10; //allocated on the heap x = 11;
println!("The Heaps new value is {}, x");}
//Cargo run//error: mismatched types: expected `Box<int>`// x = 11;// ^~~~~~~
memory mutability
fn main() {
let x: Box<int> = box 10; //allocated on the heap *x = 11;
println!("The Heaps new value is {}, x");}
//Cargo run//error: cannot assign to immutable dereference of `*x`// x = 11;// ^~~~~~~
memory mutability
fn main() {
let mut x: Box<int> = box 10; //allocated on the heap *x = 11;
println!("The Heaps new value is {}, x");}
//> Cargo run//> The Heaps new value is 11//
compiler protects you by owning de-allocation
fn main() { let mut x: Box<int> = box::new(10); //allocated on the heap *x = 11;
println!("The Heaps new value is {}, x");
//Scope for x ends here so the compiler adds the de-allocation //free(x);}
//> Cargo run//> The Heaps new value is 11//
There is no garbage collection in Rust. The compiler observes the lifetime of a variable and de-allocates it where it is no longer used.
but these features don't sum to the promised memory safety
borrowing
fn main() { // x is the owner of the integer, which is memory on the stack. let x = 5;
// you may lend that resource to as many borrowers as you like let y = &x; let z = &x;
// functions can borrow too! foo(&x);
// we can do this alllllll day! let a = &x;}
ownership is singular
fn main() { let mut x = 5; let y = &mut x; //mutability only allows one borrower let z = &mut x; // ... no go}
//> Cargo build//> error: cannot borrow `x` as mutable more than once at a time// let z = &mut x;// ^~~~~~~~~~~~~~~
ownership can be transferred
fn main() { let x: Box<i32> = Box::new(5); let y = add_one(x);
println!("{}", y);}
fn add_one(mut num: Box<i32>) -> Box<i32> { *num += 1; num}
//> Cargo run//> 6//
ownership can be transferred back
fn main() { let mut x: Box<i32> = Box::new(5); x = add_one(x);
println!("{}", x);}
fn add_one(mut num: Box<i32>) -> Box<i32> { *num += 1; num}
//> Cargo run//> 6//
now THAT sums to to the promised memory safety and data race prevention
Other features of Rust
pattern matching
fn main() { let x = 5;
match x { 1 => println!("one"), 2 => println!("two"), 3 => println!("three"), 4 => println!("four"), 5 => println!("five"), _ => println!("something else"), }}
pattern matching
fn main() { let x = 5;
match x { 1 | 2 => println!("one or two"), 3 => println!("three"), 4 ... 7 => println!("4 through 7"), _ => println!("anything"), }}
pattern matching
fn main(){ struct Point { x: i32, y: i32, }
let origin = Point { x: 0, y: 0 };
match origin { Point { x: x, .. } => println!("x is {}", x), }}
//> Cargo run//> x is 0//
iterators & adapters
fn main(){ for i in range(1i, 10i).filter(|&x| x % 2 == 0) { println!("{}", i); }}
//> Cargo run//> 2//> 4//> 6//> 8
functions
//a function that takes and integer and returns an integerfn add_one(x: i32) -> i32 { x + 1}
fn main(){ println!("1 plus 1 is {}", add_one(1));}
//> Cargo run//> 1 plus 1 is 2//
closures
fn main() { let add_one = |x| { 1 + x };
println!("The sum of 5 plus 1 is {}.", add_one(5));}
//> Cargo run//> The sum of 5 plus 1 is 6.//
closures can be passed as params
//Generic function that takes a closure as an argumentfn twice<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 { f(x) + f(x)}
fn main() { let result = twice(5, |x: i32| { x * x });
println!("And we have .... {}", result);}
//> Cargo run//> And we have .... 50//
traitsstruct Circle { x: f64, y: f64, radius: f64,}
trait HasArea { fn area(&self) -> f64;}
impl HasArea for Circle { fn area(&self) -> f64 { std::f64::consts::PI * (self.radius * self.radius) }}
fn main() { let c = Circle {x:0.0, y:1.0, radius: 2.0}; println!("The circles's radious is {}", c.area());}
traits with generics
trait HasArea { fn area(&self) -> f64;}
fn print_area<T: HasArea>(shape: T) { println!("This shape has an area of {}", shape.area());}
fn main() { let c = Circle {x:0.0, y:1.0, radius: 2.0}; print_area(c);}
//> Cargo run//> This shape has an area of 12.566371//
So ... no classes?
concurrency
fn print_message(){ println!("Hello from within a thread!");}
fn main() { spawn(print_message);}
//> Cargo run//> Hello from within a thread!//
concurrency and ownership
fn print_message(){ println!("Hello from within a thread!");}
fn main() { let x: int = 5; spawn(move || print_message); x = 10;}
//> Cargo run//> error: re-assignment of immutable variable `x`// x = 10; // ^~~~~~~
The promised land of the Rust compiler :)
Cargo also has support for testing
1. unit testing2. integration testing3. benchmark testing
testing
#[test]fn adding_one(){ let expected: int = 5; let actual: int = 4;
assert_eq!(expected,actual);}
//> Cargo Test//> running 1 tests//> test adding_one ... FAILED//
testing an expected failure
#[test]#[should_fail]fn adding_one(){ let expected: int = 5; let actual: int = 4;
assert_eq!(expected,actual);}
//> Cargo Test//> running 1 tests//> test adding_one ... OK//
making it pass
#[test]fn adding_one(){ let expected: int = 5; let actual: int = 5;
assert_eq!(expected,actual);}
//> Cargo Test//> running 1 tests//> test adding_one ... OK//
benchmark tests
#[bench] fn bench_add_two(b: &mut Bencher) {
let add_one = |x| { 1 + x };
b.iter(|| add_one(2)); }
//> Cargo Test//> running 1 tests//> test tests::bench_add_two ... bench: 1 ns/iter (+/- 0)//> test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured//
As promised Rust
1. guarantees memory safety2. threads without dataraces3. zero-cost abstractions (done at compile time)4. trait-based generics5. pattern matching6. type inference7. & more
additional links
1. http://www.rust-lang.org—Guide (nice intro)—Reference (deeper articles)
2. http://rustbyexample.com3. irc (super friendly and helpful lot)
lastlycurl -s https://static.rust-lang.org/rustup.sh | sudo sh
Questions?
Anthony Broad-Crawford@[email protected]
650.691.5107 (c)