This document discusses why Rust is a useful programming language. It provides an introduction to Rust, highlighting its memory safety features, ownership and borrowing system, and functional programming aspects like iterators and closures. Examples are given to demonstrate how Rust prevents common bugs like dangling pointers and iterator invalidation. The talk also covers Rust's type system, enums, patterns matching, and its Cargo package manager.
2. Outline
1 Introduction
2 Some Motivating Examples
3 A Little Rust
4 Functional programming features
5 Closing comments
Mats Kindahl Why Rust? <2018-09-08 Sat> 2 / 44
3. Introduction
Who am I?
Senior Software Engineer at Timescale
Earlier work involves
Compiler Implementation (C/C++)
Databases (MySQL)
Real-Time Media (Video Conferencing Backend)
Long time C/C++ programmer
. . . and a bunch of other languages
You can nd me on LinkedIn
Mats Kindahl Why Rust? 2018-09-08 Sat 4 / 44
4. Introduction
What is Rust
Rust is a systems programming language that runs blazingly fast,
prevents segfaults, and guarantees thread safety.
www.rust-lang.org
Mats Kindahl Why Rust? 2018-09-08 Sat 5 / 44
5. Introduction
Rust Language Features
Ownership system prevent data races (more about that later)
Powerful type system (inspired by Haskell?)
Parameterized types (Haskell: type parameters, C++: templates)
Traits (Haskell: type classes, Go: interfaces)
Enum (Haskell: algebraic data types)
Powerful macro system (inspired by Scheme?)
FFI for integration with other languages (mainly C)
Mats Kindahl Why Rust? 2018-09-08 Sat 6 / 44
6. Introduction
History of Rust
2006: Started by Graydon Hoare (Mozilla)
2009: Mozilla got involved
May 2015: First stable release (1.0)
December 2018: Rust 2018 (1.31)
September 2019: Current stable release (1.37)
Mats Kindahl Why Rust? 2018-09-08 Sat 7 / 44
7. Introduction
Rust Governance
Open Source License (MIT, Apache Version 2)
Release train model with six weeks cycle:
nightly Generated every night
beta Branched from nightly every six weeks
stable Promoting beta after six weeks of testing
Transparant language development
Language changes using RFCs which are openly debated
Roadmap developed from RPCs on a yearly basis
Mats Kindahl Why Rust? 2018-09-08 Sat 8 / 44
9. Some Motivating Examples
Disclaimer
These examples are simplistic and for presentation purposes
Intended to show what Rust attemt so solve
Real world is never this simple
Assumption is that unsafe is not used.
Mats Kindahl Why Rust? 2018-09-08 Sat 11 / 44
10. Some Motivating Examples
Implicit numerical conversions
#include cstdio
int main() {
// Oops, used int instead of float
int x = 10.5;
printf(%d, x);
}
Mats Kindahl Why Rust? 2018-09-08 Sat 12 / 44
11. Some Motivating Examples
Implicit truncation
#include cstdio
#include cmath
int main() {
// Oops, used float instead of double
const float x = acos(-1);
// Will print value is 3.1415927410
printf(value is %fn, x);
}
Mats Kindahl Why Rust? 2018-09-08 Sat 13 / 44
12. Some Motivating Examples
Dangling pointers in C++
int* some_function() {
int x = 47;
return x; // -- Compiler warning
}
int main() {
auto x = some_function();
*x = 4711; // -- Segmentation fault
}
Mats Kindahl Why Rust? 2018-09-08 Sat 14 / 44
13. Some Motivating Examples
Dangling pointers in Rust
fn some_function() - 'static i32 {
let x : i32 = 47;
return x; // -- Compiler error
}
fn main() {
let ptr = some_function();
*ptr = 4711;
}
Mats Kindahl Why Rust? 2018-09-08 Sat 15 / 44
14. Some Motivating Examples
Dangling pointers in Rust
error[E0515]: cannot return reference to local variable `x`
-- /tmp/babel-8gQTAg/rust-P9bKjb:4:10
|
4 | return x; // -- Compiler error
| ^^ returns a reference to data owned by the current function
error[E0594]: cannot assign to `*ptr` which is behind a `` reference
-- /tmp/babel-8gQTAg/rust-P9bKjb:9:3
|
8 | let ptr = some_function();
| --- help: consider changing this to be a mutable reference: `mut i32`
9 | *ptr = 4711;
| ^^^^^^^^^^^ `ptr` is a `` reference, so the data it refers to cannot be written
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0515`.
Mats Kindahl Why Rust? 2018-09-08 Sat 16 / 44
15. Some Motivating Examples
Dangling pointers in C++
void do_stuff(MyClass *object) {
delete object;
}
int main() {
auto object = new MyClass;
do_stuff(object);
std::cout *object std::endl;
}
Previous example give warnings with -Wall and -Wpedantic.
This example will not generate a warning.
Mats Kindahl Why Rust? 2018-09-08 Sat 17 / 44
16. Some Motivating Examples
Iterator example
#include iostream
#include string
#include vector
int main() {
std::vectorstd::string v;
v.push_back(first);
auto x = v.begin();
v.push_back(second);
std::cout *x std::endl;
}
Mats Kindahl Why Rust? 2018-09-08 Sat 18 / 44
17. Some Motivating Examples
Iterator invalidation in C++
Iterators can be invalidated because of container changes
Example: push_back can (but does not have to) invalidate all iterators.
Accessing an invalid iterator can reference unknown memory
Example: if a std::vector is moved as a result of a resize
This is true in both C++ and Rust
Mats Kindahl Why Rust? 2018-09-08 Sat 19 / 44
18. Some Motivating Examples
Iterator example in Rust
fn main() {
let mut v = vec![];
v.push(first);
let x = v[0];
v.push(second); // -- Compiler error
println!({}, x);
}
Mats Kindahl Why Rust? 2018-09-08 Sat 20 / 44
19. Some Motivating Examples
Iterator invalidation
error[E0502]: cannot borrow `v` as mutable because it is
also borrowed as immutable
-- iterator.rs:5:5
|
4 | let x = v[0];
| - immutable borrow occurs here
5 | v.push(world);
| ^ mutable borrow occurs here
6 | println!({}, x);
7 | }
| - immutable borrow ends here
Mats Kindahl Why Rust? 2018-09-08 Sat 21 / 44
20. A Little Rust
Introductory example
fn dot_product(x: Vecf64, y: Vecf64) - f64 {
let mut result: f64 = 0.0;
for i in 0 .. x.len() {
result += x[i] * y[i];
}
return result;
}
fn main() {
let x = vec![1.0, 2.0, 3.0];
let y = vec![2.0, 4.0, 6.0];
println!(Result: {}, dot_product(x, y));
}
Mats Kindahl Why Rust? 2018-09-08 Sat 23 / 44
21. A Little Rust
Memory Safety
Rust provide memory safety without garbage collection
No explicit memory allocation
No null pointers (!)
Borrow checker to avoid accessing invalid data
It is not possible to accidentally:
Access uninitialized data
Use dangling pointers
Use deleted memory or delete it twice
Use invalidated iterators
Mats Kindahl Why Rust? 2018-09-08 Sat 24 / 44
22. A Little Rust
Variables have scope
fn do_stuff(object: MyClass) {
// Do something
}
fn main() {
let object = MyClass::new();
do_stuff(object);
println!({}, object);
// 'object' goes out of scope, will be destroyed
}
No explicit delete.
Variables going out of scope will be destroyed.
Closely related to variable ownership (covered soon)
Mats Kindahl Why Rust? 2018-09-08 Sat 25 / 44
23. A Little Rust
Optional values
fn find(list: Veci32, value: i32) - Optionusize {
for i in 1..list.len() {
if list[i] == value {
return Some(i);
}
}
return None;
}
fn main() {
let list = vec![6,3,8,7,3];
if let Some(idx) = find(list, 7) {
println!(Found {} at index {}, 7, idx);
}
}
Mats Kindahl Why Rust? 2018-09-08 Sat 26 / 44
24. A Little Rust
Ownership and borrowing
... and the borrow checker
Ownership and borrowing
Central to the memory safety features of Rust
Allow Rust to not do garbage collection
Ownership and borrowsing prevents data races
More than one concurrent access to an object
One access is a write
The accesses are not synchronized
Ownership and borrow rules checked at compile time
Mats Kindahl Why Rust? 2018-09-08 Sat 27 / 44
25. A Little Rust
Ownership
struct Dimen {
height: u32,
width: u32,
}
fn main() {
let dim = Dimen { height: 400, width: 600 };
println!(dim: {}x{}, dim.width, dim.height);
} // 'dim' scope ends here: will be destroyed
Every value is owned by a variable.
There is only a single owner at each time.
When the owner goes out of scope, the value is dropped.
Ownership can be transferred to other variables.
Mats Kindahl Why Rust? 2018-09-08 Sat 28 / 44
26. A Little Rust
Ownership
Move semantics: changing owner of values
fn show_dimen(dim: Dimen) {
println!({}x{}, dim.width, dim.height);
} // 'dim' scope ends here: will be destroyed
fn main() {
let dim = Dimen { height: 400, width: 600 };
show_dimen(dim);
println!(height: {}, dim.height); // -- Compiler error
}
Mats Kindahl Why Rust? 2018-09-08 Sat 29 / 44
27. A Little Rust
Borrowing
Immutable borrow: temporary read access to value
fn show_dimen(dim: Dimen) {
println!({}x{}, dim.height, dim.width);
}
fn main() {
let data = Dimen { height: 400, width: 600 };
show_dimen(data);
show_dimen(data);
}
Mats Kindahl Why Rust? 2018-09-08 Sat 30 / 44
28. A Little Rust
Borrowing
Mutable borrow: temporary read/write access to value
fn scale_to(dim: mut Dimen, percent: u32) {
dim.height = dim.height * percent / 100;
dim.width = dim.width * percent / 100;
}
fn main() {
let mut data = Dimen { height: 400, width: 600 };
scale_to(mut data, 50);
show_dimen(data);
}
Mats Kindahl Why Rust? 2018-09-08 Sat 31 / 44
29. A Little Rust
Borrowing
Rules
1 You have a single mutable borrow of a value or
2 You have one or more immutable borrows of a value.
3 The lifetime of a borrow may not exceed the lifetime of the owner.
When borrowing and moving does not cut it:
Move semantics is default (cheapest)
Borrow semantics where moving is a problem (cheap)
Copy semantics when borrow is a problem (moderate)
Clone semantics when copy is a problem (can be expensive)
Mats Kindahl Why Rust? 2018-09-08 Sat 32 / 44
30. Functional programming features
Enumerations
enum Error {
Aborted,
NotFound(String),
Internal{code: u32, msg: String},
}
More similar to std::variant than C/C++ enum.
Used where enum and union are used in C/C++.
Mats Kindahl Why Rust? 2018-09-08 Sat 34 / 44
31. Functional programming features
Enumerations
Pattern matching
fn to_string(err: Error) - String {
match err {
Error::Aborted = format!(aborted),
Error::NotFound(key) = format!('{}' not found, key),
Error::Internal{code, ref msg} if code != 0 =
format!(internal[{}]: {}, code, msg),
_ = panic!(should not come here)
}
}
Enumerations can be pattern matched
Matching is exhaustive: compiler error if enum not covered
Matches can also be conditional
Mats Kindahl Why Rust? 2018-09-08 Sat 35 / 44
32. Functional programming features
Closures
fn main() {
let incr = 4;
let adder = |val| { val + incr };
for x in 1..10 {
println!(adder({}): {}, x, adder(x));
}
}
Anonymous functions (similar to lambda in C++)
Implicitly captures variables (in contrast to C++)
Default capture is by borrowing
Move capture can be specied
Mats Kindahl Why Rust? 2018-09-08 Sat 36 / 44
33. Functional programming features
Iterators
fn main() {
let v = vec![10,12,32,1,5];
for e in v {
println!(e: {}, e);
}
}
Iterate over a collection (for example) as a sequence
You can dene iterators for custom data types
Mats Kindahl Why Rust? 2018-09-08 Sat 37 / 44
34. Functional programming features
Iterators and Closures
fn main() {
let v = vec![1,19,2,5];
let vec_plus_1: Vec_ = v.iter().map(|x| x+1).collect();
println!({:?}, vec_plus_1);
}
Closures are very useful with iterators
Many methods for iterators taking closures
Iterators are lazy: evaluate to get a result
Iterators and closures often faster than for-loops
Mats Kindahl Why Rust? 2018-09-08 Sat 38 / 44
35. Functional programming features
Chaining iterators
A more complicated example
fn dot_product(x: [f64], y: [f64]) - f64 {
x.iter().zip(y).map(|(a, b)| a * b).sum()
}
fn main() {
let x = [1.0, 2.0, 3.0];
let y = [2.0, 4.0, 6.0];
println!(Result: {}, dot_product(x, y));
}
Mats Kindahl Why Rust? 2018-09-08 Sat 39 / 44
36. Closing comments
Cargo
The Rust project manager
Manages dependencies
Documentation
Similar to JavaDoc
Testing
Unit tests
Integration tests
Documentation tests
Benchmarking
Creates and uploads packages
Integrates with crates.io
Mats Kindahl Why Rust? 2018-09-08 Sat 41 / 44
37. Closing comments
Cargo
~$ cargo new oyster --bin
Created binary (application) `oyster` project
~$ cd oyster/
~/oyster$ cargo build
Compiling oyster v0.1.0 (file:///home/matski/oyster)
Finished dev [unoptimized + debuginfo] target(s) in 0.47s
~/oyster$ cargo test
Compiling oyster v0.1.0 (file:///home/matski/oyster)
Finished dev [unoptimized + debuginfo] target(s) in 0.50s
Running target/debug/deps/oyster-5a63e052e9010699
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Mats Kindahl Why Rust? 2018-09-08 Sat 42 / 44
38. Closing comments
Crates.io
The Rust package registry
Community-driven
Contains approx. 19,000
crates
Integrates with Cargo
serde serialization and deserialization
of objects
disel object-relational (ORM)
mapping for databases
log logging
rand random number generation
tokio asynchronous, event-driven
platform
Mats Kindahl Why Rust? 2018-09-08 Sat 43 / 44