Rust: Reach Further
- 4. 4
Safety =
Eat your spinach!
Photo credit: Sanjoy Ghosh
https://www.flickr.com/photos/sanjoy/4016632253/
- 5. 5
Photo credit: Salim Virji
https://www.flickr.com/photos/salim/8594532469/
Safety = Eat your spinach!
- 6. 6
The Rust compiler just saved me from a nasty threading bug. I was
working on cage (our open source development tool for Docker apps with
lots of microservices), and I decided to parallelize the routine that
transformed docker-compose.yml files.
- 8. 8
Fast
Zero-cost abstractions:
High-level code, low-level efficiency
No garbage collector:
Predictable, memory usage; no pauses
No compiler heroics needed
Apply techniques to other resources (sockets, etc)
Indeed, no mandatory runtime at all:
Embedded, WASM, or standalone libraries
- 10. static VALUE
rb_str_blank_as(VALUE str)
{
rb_encoding *enc;
char *s, *e;
enc = STR_ENC_GET(str);
s = RSTRING_PTR(str);
if (!s || RSTRING_LEN(str) == 0) return Qtrue;
e = RSTRING_END(str);
while (s < e) {
int n;
unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
switch (cc) {
case 9:
case 0xa:
case 0xb:
case 0xc:
case 0xd:
case 0x20:
case 0x85:
case 0xa0:
case 0x1680:
case 0x2000:
case 0x2001:
case 0x2002:
case 0x2003:
case 0x2004:
case 0x2005:
case 0x2006:
case 0x2007:
case 0x2008:
case 0x2009:
case 0x200a:
case 0x2028:
case 0x2029:
case 0x202f:
case 0x205f:
case 0x3000:
#if ruby_version_before_2_2()
case 0x180e:
#endif
/* found */
break;
default:
return Qfalse;
}
s += n;
}
return Qtrue;
}
Performance
Ruby:
964K iter/sec
C:
10.5M iter/sec
10x!
https://github.com/SamSaffron/fast_blank
- 11. Performance
11
class ::String
def blank?
/A[[:space:]]*z/ == self
end
end
extern “C” fn fast_blank(buf: Buf) -> bool {
buf.as_slice().chars().all(|c| c.is_whitespace())
}
Get Rust
string slice
Get iterator over
each character
Are all characters
whitespace?
Rust:
11M iter/sec
Ruby:
964K iter/sec
C:
10.5M iter/sec
- 12. 12
High-level, zero-cost abstractions
fn is_whitespace(text: &str) -> bool {
text.chars()
.all(|c| c.is_whitespace())
}
fn load_images(paths: &[PathBuf]) -> Vec<Image> {
paths.par_iter()
.map(|path| Image::load(path))
.collect()
}
- 22. fn main() {
let mut book = Vec::new();
book.push(…);
book.push(…);
publish(book);
}
fn publish(book: Vec<String>) {
…
}
Ownership
Take ownership
of the vector
22
Error: use of moved
value: `book`
String
book
data
length
capacity
[0]
[1]
data
length
capacity
~~~~~~~~~~~
Give ownership.
publish(book);
- 23. 23
“Manual” memory management in Rust:
Values owned by creator.
Values moved via assignment.
When final owner returns, value is freed.
Feels
invisible.]
- 24. void main() {
Vector book = …;
publish(book);
publish(book);
}
void publish(Vector book) {
…
}
“Ownership” in Java
Take reference
to Vector
24
new Thread(…);JavaCode
- 25. ~ Ownership and borrowing ~
Type Ownership
T
Alias? Mutate?
Owned ✓
&T Shared reference ✓
- 26. fn lender() {
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
use(&vec);
…
}
fn use(vec: &Vec<int>) {
}
1
2vec
data
length
capacity
vec
“Shared reference
toVec<int>”
Loan out vec
…
- 27. ~~~~~~~~~
let mut book = Vec::new();
book.push(…);
let r = &book;
book.len();
book.push(…);
r.push(…);
book.push(…);
reading `book` ok while shared
cannot mutate while shared
27
Sharing “freezes” data (temporarily)
`book` mutable here
~~~~~~~~~~~
`book` borrowed here
after last use of `r`,
`book` is mutable again
cannot mutate through shared ref
- 28. ~ Ownership and borrowing ~
Type Ownership
T
&T
Alias? Mutate?
Owned
Shared reference
✓
✓
&mut T Mutable reference ✓
- 29. fn main() {
let mut book = Vec::new();
book.push(…);
book.push(…);
edit(&mut book);
edit(&mut book);
}
fn edit(book: &mut Vec<String>) {
book.push(…);
}
Mutable borrow
Mutable reference
to a vector
29
String
book
data
length
capacity
[0]
[1]
book
[2]
Mutable borrow
[3]
- 30. cannot access while borrowed
but can mutate through `r`
30
Mutable references: no other access
book mutable here
~~~~~~~~~
book borrowed here
after last use of `r`,
book is accessible again
let mut book = Vec::new();
book.push(…);
let r = &mut book;
book.len();
r.push(…);
book.push(…);
- 31. 31
Definition: a closure is a callback that Just Works.
— The Reinvigorated Programmer
https://reprog.wordpress.com/2010/02/27/closures-finally-explained/
~ Closures ~
- 32. 32
0counter
creates a closure
closure borrows `counter` from
enclosing stack frame
closure
1
cannot access while borrowed
done using closure; ok
2 3
~~~~~~~~~~~
fn main() {
let mut counter = 0;
let mut closure = || {
counter += 1;
};
closure();
counter += 1;
closure();
counter += 1;
}
- 33. 33
There are 2 hard problems in computer science:
cache invalidation, naming things, and off-by-one
errors.
— Leon Bambrick
~ Named lifetimes ~
- 34. 34
impl<T> [T] {
fn split_at_mut(
&’a mut self,
mid: usize,
) -> (&’a mut [T], &’a mut [T]) {
…
}
}
given a slice of T elements…
[0] [1] [2] [3] […] [n]
self: &mut [i32]
less: &mut [i32] greater: &mut [i32]
and a midpoint
divide slice into two
fn foo(vec: &mut [T]) {
let (less, greater) =
vec.split_at_mut(3);
…
} `vec` borrowed here
while `less` and
`greater` still in use
- 36. 36
Observation:
Building parallel abstractions is easy.
Misusing those abstractions is also easy.
func foo(…) {
m := make(map[string]string)
m[“Hello”] = “World”
channel <- m
m[“Hello”] = “Data Race”
}
send data over channel
but how to stop sender from
using it afterwards?
GoCode
- 37. 37
fn foo(…) {
let m = HashMap::new();
m.insert(“Hello”, “World”);
channel.send(m);
m.insert(“Hello”, “Data Race”);
}
impl<T> Channel<T> {
fn send(&mut self, data: T) {
…
}
}
Take ownership
of the data
Error: use of moved
value: `book`
~~~~~~~~~~~~~~~~~~~~~~~~~~
- 39. 0
fn sync_inc(mutex: &Mutex<i32>) {
let mut guard: Guard<i32> = counter.lock();
*guard += 1;
}
https://commons.wikimedia.org/wiki/File:No-DRM_lock.svg
1
mutex
guard
- 42. [0] [1] [2] [3] […] [n]
vec: &mut [i32]
less: &mut [i32] greater: &mut [i32]
fn qsort(vec: &mut [i32]) {
if vec.len() <= 1 { return; }
let pivot = vec[random(vec.len())];
let mid = vec.partition(vec, pivot);
let (less, greater) = vec.split_at_mut(mid);
qsort(less);
qsort(greater);
}
- 43. fn qsort(vec: &mut [i32]) {
if vec.len() <= 1 { return; }
let pivot = vec[random(vec.len())];
let mid = vec.partition(vec, pivot);
let (less, greater) = vec.split_at_mut(mid);
rayon::join(
|| qsort(less),
|| qsort(greater),
);
}
[0] [1] [2] [3] […] [n]
vec: &mut [i32]
less: &mut [i32] greater: &mut [i32]
rayon::join(
|| qsort(less),
|| qsort(less),
);
?~~~~~~~~~
- 44. ~ Concurrency paradigms ~
Paradigm
Message passing
Ownership? Borrowing?
✓
Locking
Fork join
✓ ✓
✓
Lock-free
Futures
…
✓ ✓
✓ ✓
- 46. 46
Vision: An Extensible Language
Core language:
Ownership and borrowing
Libraries:
Reference-counting
Files
Parallel execution
…
Use ownership/
borrowing to enforce
correct usage.
]
- 48. 48
Stylo (Parallel CSS Rendering — coming in FF57)
Total KLOC Unsafe KLOC Unsafe %
Total 146.2 51.7 35%
Interesting stuff 71.6 1.4 1.9%
FFI Bindings 74.5 50.3 67.4%
- 56. 56
Rust Leadership Structure
Team Members Peers
Core 9
Language 6 5
Libraries 7 1
Compiler 9
Dev Tools 6 11
Cargo 6
Infrastructure 10
Community 13
Documentation 4
Moderation 5
58 people
10 Mozilla (17%)
- 57. Our responsibility [after 1.0] is to ensure
that you never dread upgrading Rust.
Since the early days of Rust, there have only been
two things you could count on: safety, and change.
And sometimes not the first one.
Rust 1.0: Stability as a deliverable
- 60. 60
Want to learn more?
intorust.comrust-lang.orgO’Reilly
Book, 2nd ed. ScreencastsPre-order now!