Hey, you! If you have yet to install Rust:
Run `rustc --version` to ensure that installation was successful
Ben Striegel
@bstrie
// C
int sum = 0;
for (int i=0; i<x; i++) {
sum += i;
}
# Python
sum = 0
for i in range(0, x):
sum += i
# `sum()` ist verboten
// Rust
let mut sum = 0;
for i in 0..x {
sum += i;
}
# Ruby
(0...x).inject(0) {|sum, i| sum + i}
# Python
reduce(lambda sum, i: sum + i,
range(0, x), 0)
// Rust
(0..x).fold(0, |sum, i| sum + i)
; Assembly
lea eax, [rdi - 1]
lea ecx, [rdi - 2]
imul rcx, rax
shr rcx
lea eax, [rcx + rdi - 1]
// Rust
(0..x).fold(0, |sum, i| sum + i)
// C, runtime memory error
int* foo() {
int x = 4;
return &x;
}
// Go, safe thanks to GC
func foo() *int {
x := 4
return &x
}
// Rust, does not compile
fn foo() -> &i32 {
let x = 4;
return &x;
}
// Rust, does not compile
fn foo() -> &i32 {
let x = 4;
return &x;
}
error[E0106]: missing lifetime specifier --> foo.rs:1:13 | 1 | fn foo() -> &i32 { | ^ expected lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
// C++, invalidated iterator
for(auto i: v) {
if (i > 0) {
v.push_back(i * -1);
}
}
// Java, runtime exception
for (Integer i: v) {
if (i > 0) {
v.add(i * -1);
}
}
// Rust, does not compile
for i in v {
if i > 0 {
v.push(i * -1);
}
}
error[E0382]: use of moved value: `v` --> invalidator.rs:3:9 | 1 | for i in v { | - value moved here 2 | if i > 0 { 3 | v.push(i * -1); | ^ value used here after move
// Go, with data race
x := 0
for i := 0; i < 10; i++ {
go func() { x += 1 }()
}
// Go, without data race
mutex := &sync.Mutex{}
x := 0
for i := 0; i < 10; i++ {
go func() {
mutex.Lock()
x += 1
mutex.Unlock()
}()
}
// Rust, does not compile
let mut x = 0;
for i in 0..10 {
spawn(|| x += 1);
}
error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function --> race.rs:3:11 | 3 | spawn(|| x += 1); | ^^ - `x` is borrowed here | | | may outlive borrowed value `x`
// Rust
let x = Arc::new(Mutex::new(0));
for i in 0..10 {
let y = x.clone();
spawn(move || {
let mut z = y.lock().unwrap();
*z += 1;
});
}
Things to note:
fn main() {
println!("Hello!");
}
// C, Javascript
while (a) b;
for (a; c; d;) e;
for (;;) a;
if (a)
b;
else if (c) {
d;
e;
} else
f;
// Rust
while a { b; }
for a in b { c; }
loop { a; }
if a {
b;
} else if c {
d;
e;
} else {
f;
}
Signed fixed-width integers:
Unsigned fixed-width integers:
Platform-sized integers:
Floating point:
UTF-32 Unicode scalar values:
Booleans:
i8, i16, i32, i64 u8, u16, u32, u64 isize, usize f32, f64 char bool
Fixed-size array of N elements:
Growable, heap-allocated buffer:
Growable, heap-allocated buffer of UTF-8:
Tuple containing several different types:
[T; N] Vec<T> String (T, U, V)
See the `collections` module in the standard library for hashmaps, sets, B-trees, and more.
Shared reference:
Mutable reference:
Shared slice (pair of pointer and length):
Mutable slice:
String slice:
Heap-allocated smart pointer:
Reference-counted smart pointer:
Concurrent reference-counted smart pointer:
&T &mut T &[T] &mut [T] &str Box<T> Rc<T> Arc<T>
let a = 1; // integer
let b = 2.0; // float
let c = "three"; // static string slice
let d = '4'; // char
let e = [5, 6, 7]; // fixed-size array
let f = (8.0, "nine", 10); // tuple
let g = vec![11, 12, 13]; // vector
Create new library project:
Create new application project:
Build and run in debug mode:
Build and run in release mode:
Build without running:
Run your test suite:
Update dependencies:
View command-line options:
cargo new foo cargo new foo --bin cargo run cargo run --release cargo build cargo test cargo update cargo help
Get the code at github.com/jimblandy/exercises
If you have git:
git clone https://github.com/jimblandy/exercises.git
If you don't have git, download and unzip:
https://github.com/jimblandy/exercises/archive/master.zip
Navigate into the exercises/ex1 directory and run `cargo test`
Observe the test failures
Edit the `is_prime` function in src/lib.rs until all tests pass
OPTIONAL: set up Rust support for your favorite code editor by perusing www.rust-lang.org/ides
1. All variables are owned by their enclosing scope
2. When a scope ends, everything it owns is deallocated
3. When one variable is assigned to another variable, the original variable becomes inaccessible, because a piece of memory must have only one owner at a time
4. Passing a variable to a function will move ownership of that data into that function's scope, and make the original variable inaccessible
5. Returning a value from a function or scope will move ownership of that data into the enclosing scope
1. All variables are owned by their enclosing scope
fn foo() { // this scope owns x and z
let x = Foo;
if true { // this scope owns y
let y = Bar;
}
let z = Qux;
}
2. When a scope ends, everything it owns is deallocated
fn foo() {
let x = Foo;
if true {
let y = Bar;
} // y is deallocated
let z = Qux;
} // x and z are deallocated
3. When one variable is assigned to another variable, the original variable becomes inaccessible, because a piece of memory must have only one owner at a time
let x = Foo;
let y = x;
// COMPILATION ERROR BELOW
do_anything_with(x);
error[E0382]: use of moved value: `x` --> three.rs:3:18 2 | let y = x; | - value moved here 3 | do_anything_with(x); | ^ value used here after move
4. Passing a variable to a function will move ownership of that data into that function's scope, and make the original variable inaccessible
let x = Foo;
now_ur_mine(x);
// COMPILATION ERROR BELOW
let y = x;
error[E0382]: use of moved value: `x` --> four.rs:3:5 2 | now_ur_mine(x); | - value moved here 3 | let y = x; | ^ value used here after move
5. Returning a value from a function or scope will move ownership of that data into the enclosing scope
fn bar() -> Foo {
let x = Foo;
return x;
}
let y = bar();
// Putting it all together...
fn pass_it_through(y: Foo) -> Foo {
return y;
}
fn end_of_the_line(z: Foo) {
// memory_deallocated_here
}
let x = Foo; // memory allocated here
let y = x;
let z = pass_it_through(y);
end_of_the_line(z);
There is one and only one way to alter the behavior of ownership: by making a type copyable
Once a type is copyable, moves will no longer render the original variable inaccessible
This is the complete extent of the user's ability to hijack the ownership system itself, i.e. there are no user-defined copy constructors as there are in C++
This mechanism exists because there's really no benefit to treating small, boring data types like booleans or integers as uniquely-owned pieces of data
// Traits as interfaces
trait DoesSomething {
fn do_something(self);
}
impl DoesSomething for i32 {
fn do_something(self) {
println!("Extending basic types!");
}
}
6.do_something();
fn not_generic(x: i32) -> i32 {
return x;
}
fn totes_generic<T>(x: T) -> T {
return x;
}
// Traits as bounds on generics
trait DoesSomething {
fn do_something(self);
}
fn kinda_useless_generic<T>(x: T) {
// we basically can't do anything with x,
// because we don't know what it allows!
}
fn useful_generic<T: DoSomething>(x: T) {
x.do_something();
}
impl DoesSomething for i32 {
fn do_something(self) { println!("OMG!"); }
}
impl DoesSomething for bool {
fn do_something(self) { println!("ZOMG!!"); }
}
fn kinda_useful_generic<T: DoSomething>(x: T) {
x.do_something();
}
kinda_useful_generic(9); // OMG!
kinda_useful_generic(true); // ZOMG!!
// A typical struct
struct Foo {
w: i32,
x: usize
}
// A generic struct
struct Bar<T, U> {
y: T,
z: U
}
struct Foo<T> {
my_anything: T,
my_num: i32
}
let bar = Foo { my_anything: "yo", my_num: 2 };
println!("{}", bar.my_anything);
fn spam() -> i32 { return 5; }
let qux = Foo { my_num: 3, my_anything: spam };
println!("{}", (qux.my_anything)()); // 5
use MagicSpell::{Fireball, BalefulPolymorph};
enum MagicSpell {
Fireball,
BalefulPolymorph
}
fn make_spell_sound(spell: MagicSpell) {
match spell {
Fireball => println!("BOOM!"),
BalefulPolymorph => println!("MOO!")
}
}
use IntList::{Node, Empty};
enum IntList {
Node(i32, Box<IntList>),
Empty
}
let x = Node(2,
Box::new(Node(4,
Box::new(Empty))));
use Option::{Some, None};
// Redundant with the stdlib
enum Option<T> {
Some(T),
None
}
fn foo<T>(x: Option<T>) {
match x {
Some(y) => println!("Something!"),
None => println!("Nothing :((("),
}
}
#[derive(Debug)]
enum MagicSpell {
Fireball,
BalefulPolymorph
}
let spell = MagicSpell::Fireball;
println!("{:?}", spell);
fn main(x: Foo) -> Foo {
// man, really wish I
// didn't have to return x...
}
fn main(x: &Foo) {
// sheer ecstasy!
}
Literally the hardest thing about learning Rust as a newcomer! The dreaded Borrow Checker!! :O
SECRET PRO TIP: The borrow checker only kicks in if you're using references, or are using something that might eventually hold references. If you don't want to deal with borrow check errors, then you can just clone and box everything until you feel comfortable.
DOUBLE SECRET PRO TIP: Download the nightly compiler and pass the experimental flag for new borrow check error message!
TRIPLE SECRET PRO TIP: Just ask on IRC! They're all seasoned borrow check veterans.
Get these slides at slides.com/bstrie/rust-rbr