Professional Documents
Culture Documents
1
Agenda
1. What is Rust?
3. Reading Rust
5. Multithreaded Programming
6. Further Reading
7. Questions
2
1. What is Rust?
3
Rust is a systems programming language
4
Whats wrong with systems
languages?
- Its difficult to write secure code.
- Its very difficult to write multithreaded code.
5
Quick Facts about Rust
(As of September 2015)
6
Features
- Zero-cost abstractions
- Move semantics
- Guaranteed memory safety
- Threads without data races
- Trait based generics
- Pattern matching
- Type inference
- Minimal runtime, no GC
- Efficient C bindings
7
2. What is Type Safety?
8
A C Program
int main(int argc, char **argv) {
unsigned long a[1];
a[3] = 0x7ffff7b36cebUL;
return 0;
}
9
Definitions
- If a program has been written so that no possible execution
can exhibit undefined behavior, we say that program
is well defined.
- If a languages type system ensures that every program is well
defined, we say that language is type safe
10
Type Safe Languages
- C and C++ are not type safe.
- Python is type safe:
>> a = [0]
>>> a[3] = 0x7ffff7b36ceb
Traceback (most recent call last):
File "", line 1, in <module>
IndexError: list assignment index out of range
>>>
11
Its ironic.
C and C++ are not type safe.
12
3. Reading Rust
13
Example 1
fn gcd(mut n: u64, mut m: u64) -> u64 {
assert!(n != 0 && m != 0);
while m != 0 {
if m < n {
let t = m; m = n; n = t;
}
m = m % n;
}
n
}
14
Example 1
fn gcd(mut n: u64, mut m: u64) -> u64 {
assert!(n != 0 && m != 0);
while m != 0 {
if m < n {
let t = m; m = n; n = t;
}
m = m % n;
}
n
}
15
Example 1
fn gcd(mut n: u64, mut m: u64) -> u64 {
assert!(n != 0 && m != 0);
while m != 0 {
if m < n {
let t = m; m = n; n = t;
}
m = m % n;
}
n
}
16
Example 1
fn gcd(mut n: u64, mut m: u64) -> u64 {
assert!(n != 0 && m != 0);
while m != 0 {
if m < n {
let t = m; m = n; n = t;
}
m = m % n;
}
n
}
17
Example 1
fn gcd(mut n: u64, mut m: u64) -> u64 {
assert!(n != 0 && m != 0);
while m != 0 {
if m < n {
let t = m; m = n; n = t;
}
m = m % n;
}
n
}
18
Example 1
fn gcd(mut n: u64, mut m: u64) -> u64 {
assert!(n != 0 && m != 0);
while m != 0 {
if m < n {
let t = m; m = n; n = t;
}
m = m % n;
}
n
}
19
Example 1
fn gcd(mut n: u64, mut m: u64) -> u64 {
assert!(n != 0 && m != 0);
while m != 0 {
if m < n {
let t = m; m = n; n = t;
}
m = m % n;
}
n
}
20
Example 1
fn gcd(mut n: u64, mut m: u64) -> u64 {
assert!(n != 0 && m != 0);
while m != 0 {
if m < n {
let t = m; m = n; n = t;
}
m = m % n;
}
n
}
21
Example 1
fn gcd(mut n: u64, mut m: u64) -> u64 {
assert!(n != 0 && m != 0);
while m != 0 {
if m < n {
let t = m; m = n; n = t;
}
m = m % n;
}
n
}
22
Example 2: Generics
fn min<T: Ord>(a: T, b: T) -> T {
if a <= b { a } else { b }
}
23
Example 2: Generics
fn min<T: Ord>(a: T, b: T) -> T {
if a <= b { a } else { b }
}
24
Example 2: Generics
fn min<T: Ord>(a: T, b: T) -> T {
if a <= b { a } else { b }
}
...
25
Example 3: Generic Types
struct Range<Idx> {
start: Idx,
end: Idx,
}
...
26
Example 4: Enumerations
enum Option<T> {
Some(T),
None
}
27
Example 5: Application of Option<T>
fn safe_div(n: i32, d: i32) -> Option<i32> {
if d == 0 {
return None;
}
Some(n / d)
}
28
Example 6: Matching an Option
match safe_div(num, denom) {
None => println!(No quotient.),
Some(v) => println!(Quotient is {}., v)
}
29
Example 7: Traits
trait HasArea {
fn area(&self) -> f64;
}
30
Example 8: Trait Implementation
struct Circle {
x: f64,
y: f64,
radius: f64,
}
32
Example 10: Trait Composition
trait Foo {
fn foo(&self);
}
33
4. Memory Safety in Rust
34
Three Key Promises
- No null pointer dereferences
- No dangling pointers
- No buffer overruns
35
P1: No null pointer dereferences
- Null pointers are useful
- They can indicate the absence of optional information
- They can indicate failures
- But they can introduce severe bugs
- Rust separates the concept of a pointer from the concept of an
optional or error value
- Optional values are handled by Option<T>
- Error values are handled by Result<T, E>
- Many helpful tools to do error handling
36
You already saw Option<T>
fn safe_div(n: i32, d: i32) -> Option<i32> {
if d == 0 {
return None;
}
Some(n / d)
}
37
Theres also Result<T, E>
enum Result<T, E> {
Ok(T),
Err(E)
}
38
How to use Results
enum Error {
DivisionByZero,
}
39
Tedious Results
fn do_calc() -> Result<i32, String> {
let a = match do_subcalc1() {
Ok(val) => val,
Err(msg) => return Err(msg),
}
let b = match do_subcalc2() {
Ok(val) => val,
Err(msg) => return Err(msg),
}
Ok(a + b)
}
40
The try! Macro
fn do_calc() -> Result<i32, String> {
let a = try!(do_subcalc1());
let b = try!(do_subcalc2());
Ok(a + b)
}
41
Mapping Errors
fn do_subcalc() -> Result<i32, String> { }
is the same as
43
Other Combinator Methods (1)
Get the value from an option.
Option.unwrap(self) -> T
Option.unwrap_or(self, def: T) -> T
Option.unwrap_or_else<F>(self, f: F) -> T
where F: FnOnce() -> T
44
Other Combinator Methods (2)
Map an Option<T> to Option<U> or U.
45
Other Combinator Methods (3)
Convert an option to a result, mapping Some(v) to Ok(v)
and None to Err(err).
Option.ok_or<E>(self, err: E)
-> Result<T, E>
Option.ok_or_else<E, F>(self, err: F)
-> Result<T, E>
where F: FnOnce() -> E
46
P2: No dangling pointers
- Rust programs never try to access a heap-allocated value after
it has been freed.
- No garbage collection or reference counting involved!
- Everything is enforced at compile time.
47
Three Rules
- Rule 1: Every value has a single owner at any given time.
- Rule 2: You can borrow a reference to a value, so long as the
reference doesnt outlive the value.
- Rule 3: You can only modify a value when you have exclusive
access to it.
48
Ownership
- Variables own their values
- A struct owns its fields
- An enum owns its values
- Every heap-allocated value has a single pointer that owns it
- All values are dropped when their owner is dropped
49
Ownership: Scoping
{
let s = Chuchichstli.to_string();
} // s goes out of scope, text is freed
50
Ownership: Move Semantics
{
let s = Chuchichstli.to_string();
51
Ownership: Copy Trait
{
let pi = 3.1415926f32;
let foo = pi;
let bar = pi; // This is fine!
}
52
Ownership: Clone Trait
{
let s = Chuchichstli.to_string();
let t1 = s.clone();
let t2 = s.clone();
}
53
Ownership: Deriving Copy / Clone
#[derive(Copy, Clone)]
struct Color { r: u8, g: u8, b: u8 }
54
But what about this?
let s = Hello, world.to_string();
print_with_umpff(s);
println!({}, s);
55
Borrowing
let s = Hello, world.to_string();
print_with_umpff(&s);
println!(Original value was {}, s);
56
Mutable Borrowing
let mut s = Hello, world.to_string();
add_umpff(&mut s);
println!(New value is {}, s);
57
Borrowing prevents moving
let x = String::new();
let borrow = &x;
let y = x; // error: cannot move out of `x` because
// it is borrowed
58
Lifetimes
let borrow;
let x = String::new();
borrow = &x; // error: `x` does not live
// long enough
59
Lifetimes
{
let borrow;
{
let x = String::new();
borrow = &x; // error: `x` does not live
// long enough
}
}
60
Lifetimes
- Sometimes the compiler is wrong about automatically
inferred lifetimes
- He needs more knowledge
- Parameters and return values can be annotated
with explicit lifetimes
- Wont be covered here :)
61
P3: No buffer overruns
- Theres no pointer arithmetic in Rust
- Arrays in Rust are not just pointers
- Bounds checks, usually at compile time (zero cost abstractions)
62
5. Multithreaded Programming
63
Well make this short
- The Rust compiler does not know about concurrency
- Everything works based on the three rules
- Ill only show a few examples
64
Threads
let t1 = std::thread::spawn(|| { return 23; });
let t2 = std::thread::spawn(|| { return 19; });
let v1 = try!(t1.join());
let v2 = try!(t2.join());
println!({} + {} = {}, v1, v2, v1 + v2);
65
Mutexes / Arcs (1)
let data = Arc::new(Mutex::new(0));
assert_eq!(*guard, 42);
67
Channels
use std::sync::mpsc::channel;
Signature:
68
6. Further Reading
69
Why Rust?
Free e-book by OReilly, ~50 pages.
Highly recommended!
http://www.oreilly.com/programming/free/why-rust.csp
70
Rust Book
Not actually a book.
Great resource.
https://doc.rust-lang.org/book/
71
7. Questions
72
Contact: +41 44 542 90 60
danilo.bargen@webrepublic.ch
mail@dbrgn.ch
73