# Rust for Scalatians
---
## What is Rust?
Blazingly fast, prevents segfaults, and guarantees thread safety (rust-lang.org)
Safe, fast, productive -- pick three ([2017 vision](https://internals.rust-lang.org/t/setting-our-vision-for-the-2017-cycle/3958))
Note:
Goal: familiarize and convince of the "safe, fast, productive" motto
---
## Pillars
Memory safety without garbage collection
Concurrency without data races
Abstraction without overhead
Stability without stagnation
Note:
Rust challenges these apparent contradictions
---

Note:
I've probably talked about rust a few too many times
---
## Why learn Rust?
- [FFI/JNI](https://github.com/Dushistov/rust_swig) for high perf components
- Zero dependency cross-platform tools
- We have [some] rust in prod
- New paradigm (not really new research)
---
## Similarities to Scala
Let's port some Scala.
Note:
Scala devs should be able to quickly feel familiar and productive
with many aspects of Rust
---
### Immutable by default
```scala
// Scala
val x = 5
x = 10 // Error: reassignment to val
var y = 5
y = 10
```
```rust
// Rust
let x = 5;
x = 10; // Error: re-assignment of immutable variable
let mut y = 5;
y = 10;
```
Note:
Minor syntax difference. Same idea.
---
## Type inferrence
```scala
// Scala
val greeting = s"Hello, $name"
val greeting: String = s"Hello, $name"
```
```rust
// Rust
let greeting = format!("Hello, {}", name);
let greeting: String = format!("Hello, {}", name);
```
Note:
Rust's type inferrence is *mostly* Hindly Milner
Pythonistas might recognize `format!` syntax as inspired by: `'Hello, {}'.format(name)`
---
### but not inferred for functions
```scala
// Scala
def foo(arg: MyType) = arg.transform
```
```rust
// Rust
fn foo(arg: MyType) -> MyOutput {
arg.transform()
}
```
Note:
Some believe "explicit" is a core tenant of Rust; That's not quite accurate.
"Predictable" is more accurate, and explicitness is often used to achieve predictability.
---
### Exhaustive Pattern Matching
```scala
// Scala
sealed abstract class Suit
case class Club extends Suit
case class Diamond extends Suit
case class Heart extends Suit
case class Spade extends Suit
card.suit match {
case Heart | Diamond => Color.Red
case Club | Spade => Color.Black
}
```
```rust
// Rust
enum Suit { Heart, Club, Spade, Diamond }
match card.suit() {
Suit::Heart | Suit::Diamond => Color::Red,
Suit::Club | Suit::Spade => Color::Black,
}
```
Note:
Enum's and exhaustive pattern matching are front-and-center in Rust
---
### [Dotty getting enums](https://github.com/lampepfl/dotty/issues/1970)
```scala
// Scala
enum Suit { case Heart, Club, Spade, Diamond }
card.suit match {
case Suit.Heart | Suit.Diamond => Color.Red
case Suit.Club | Suit.Spade => Color.Black
}
```
```rust
// Rust
enum Suit { Heart, Club, Spade, Diamond }
match card.suit() {
Suit::Heart | Suit::Diamond => Color::Red,
Suit::Club | Suit::Spade => Color::Black,
}
```
Note:
Scala will *catch up* with Rust on this front, soon. :-)
---
## Traits and Generics
```scala
// Scala
trait HasArea[T] {
def area(): T
}
case class Square(side: Int) extends HasArea[Int] {
def area() = side * side
}
```
```rust
// Rust
trait HasArea<T> {
fn area(&self) -> T;
}
struct Square { side: u32 }
impl HasArea<u32> for Square {
fn area(&self) -> u32 {
self.side * self.side
}
}
```
Note:
Rust emphasizes "composition over inheritance"
There is no "class" to extend, rather you
"implement" traits for your type.
---
### Functional iterators and closures
```scala
// Scala
val these = Array(1,2,3,4,5)
val those = Array(6,7,8,9,10)
these.zip(those)
.map{ case (i,j) => i*j }
.sum
```
```rust
// Rust
let these = vec![1,2,3,4,5];
let those = vec![6,7,8,9,10];
these.iter()
.zip(those)
.map(|(i,j)| i*j)
.sum()
```
<p class="fragment">Abstractions without overhead</p>
Note:
Now that we're operating at this beautiful high-level of abstraction,
let's pause to defend the "abstraction without overhead" claim.
---
### Zero overhead abstractions
10,000 elements in each array
| Bench | Result |
| ----- | ------ |
| C (for loop -O2) | 6,029 ns/iter |
| Rust (for loop) | 4,983 ns/iter |
| Rust (functional) | 4,984 ns/iter |
| Scala (for loop) | |
| Scala (functional) | |
[code](https://gist.github.com/anowell/9936f3b0f4ba0a8ad2c651bf9baf38ed)
Note:
Main idea: functional rust has no overhead compared to the for loop
This is NOT a claim that Rust faster than C.
-O3 flattened the C loop resulting in 4000 ns/iter.
Rust and C are in a similar performance class.
---
### Zero overhead abstractions
10,000 elements in each array
| Bench | Result |
| ----- | ------ |
| C (for loop -O2) | 6,029 ns/iter |
| Rust (for loop) | 4,983 ns/iter |
| Rust (functional) | 4,984 ns/iter |
| Scala (for loop) | 20,467 ns/iter |
| Scala (functional) | 697,099 ns/iter |
[code](https://gist.github.com/anowell/9936f3b0f4ba0a8ad2c651bf9baf38ed)
Note:
Main idea: Scala functional has a 35x overhead compared to scala for loop
This surprised me; am I supposed to choose idiomatic scala or 35x perf?
Rust vs Scala perf is not the focus here.
---
### Futures
```scala
// Scala
implicit val ec = ExecutionContext.global
val f = Future {
session.getLatestPost
}
f onComplete {
case Success(post) => println(post)
case Failure(err) => println("Error: " + err.getMessage)
}
```
```rust
// Rust (still early stage)
let pool = CpuPool::new_num_cpus();
let f = pool.spawn_fn(|| {
session.getLatestPost()
});
f.then(|res| {
match res {
Ok(post) => println!("{}", post),
Err(err) => println!("Error: {}", err),
}
})
```
Note:
Ton of work going into Rust's futures and async story
including async/await features.
---
### Futures + Async I/O
[Tokio](https://tokio.rs/) is a network application framework inspired by Twitter's [Finagle](https://twitter.github.io/finagle/guide/)
A [benchmark top competitor](https://www.techempower.com/benchmarks/#section=data-r14&hw=ph&test=plaintext)
(standard benchmarking disclaimers apply)
Note:
Tokio exemplifies and pushes the bounds of "abstraction without overhead"
Should be very interesting to watch this space evolve
---
<img src="http://i.imgur.com/8acN5PG.png" width=250>
What's the catch?
---
### FizzBuzz
Your first [impl of FizzBuzz might not work](https://chrismorgan.info/blog/rust-fizzbuzz.html).
```rust
for i in 1..100 {
let res = if i % 15 == 0 {
"fizzbuzz"
} else if i % 3 == 0 {
"fizz"
} else if i % 5 == 0 {
"buzz"
} else {
i.to_string()
};
println!("{}", res);
}
```
<p class="fragment">error: if and else have incompatible types</p>
---
### String vs str
| Type | Description |
| ---- | -------------- |
| String | Heap-allocated, growable, UTF-8 string |
| &str | Reference to UTF-8 "string slice" (could point on heap, stack, or static memory) |
--
`"fizzbuzz"` is a reference to static memory hard-coded in the binary
`i.to_string()` allocates a `String` on the heap
Note:
A good understanding of this problem requires a detour
through Rust's Ownership & Borrowing system
---
### Ownership & Borrowing
> "experienced Rust developers report that once they work with the rules of the ownership system for a period of time, they **fight the borrow checker** less and less."
Note:
Reading between the lines: inexperienced Rust developers fight the borrow checker
Also good time to check for basic understanding of stack vs heap
---
### Copy types/semantics
Special trait for automaically copied types
```rust
fn add(a: u32, b: u32) -> u32 {
a + b
}
fn main() {
let a = 5;
let b = 6;
let c = 7;
add(a, b);
add(b, c);
}
```
Simple types just work
Note:
this is often taught as the exception to the rule,
but I'm starting with it since the behavior is familiar
---
### Move semantics (owned types)
Most types "move" or "transfer ownership"
```rust
fn concat(a: String, b: String) -> String {
[a, b].concat()
}
fn main() {
let greet = "Hello ".to_string();
let alice = "Alice".to_string();
let bob = "Bob".to_string();
concat(greet, alice);
concat(greet, bob);
}
```
<p class="fragment">error: use of moved value: `greet`</p>
Note:
Contrast this with previous slide.
Code structure is the same, but `String` isn't a `Copy` type
`greet` is moved into the first call to `concat`
so it can't be used a 2nd time in `main`
---
### RAII for Owned types
Resource Acquisition Is Initialization
```rust
fn concat(a: String, b: String) -> String {
[a, b].concat()
} // 'a' and 'b' go out-of-scope and are freed
fn main() {
let greet = "Hello ".to_string();
let alice = "Alice".to_string();
let bob = "Bob".to_string();
concat(greet, alice);
concat(greet, bob);
}
```
Owned types dropped at end of scope
Note:
"dropped" means "freed" or "deallocated".
"destructor" is defined by the `Drop` trait - seldom needs manually implemented
RAII should be familiar to modern C++ devs
---
### Clone and move
Instead "move" a "clone" (deep copy)
```rust
fn concat(a: String, b: String) -> String {
[a, b].concat()
}
fn main() {
let greet = "Hello ".to_string();
let alice = "Alice".to_string();
let bob = "Bob".to_string();
concat(greet.clone(), alice);
concat(greet, bob);
}
```
Seriously?
Note:
`String` is a `Clone` type
`Clone` implies an allocation is required
Rust would NOT be such a fast language if allocating was
the default answer to satisfying the borrow checker
---
### Shared references
Borrow a shared (immutable) reference
```rust
fn concat(a: &String, b: &String) -> String {
[a, b].concat()
}
fn main() {
let greet = "Hello ".to_string();
let alice = "Alice".to_string();
let bob = "Bob".to_string();
concat(&greet, &alice);
concat(&greet, &bob);
}
```
Better.
Note:
We'll revisit this example later - we can do better yet
"Borrow" and "reference" are used interchangeably for `&`
"shared reference" == "immutable reference"
---
### Mutable references
Borrowing mutably
```rust
fn increment_all(items: &mut Vec<u32>) {
for item in items {
*item += 1;
}
}
fn main() {
let mut counts = vec![1, 2, 3, 4];
increment_all(&mut counts);
increment_all(&mut counts);
}
```
Note:
`*` is the "dereference" operator
---
### Borrow system rules
1. Single owner (move it and you lose it)
2. Multiple shared borrows xor single mutable borrow
3. Owned data must outlive any borrows
Note:
Once you internalize these rules, you'll find yourself "fighting the borrow checker" much less
...especially once NLL (non-lexical lifetimes) lands
Didn't discuss #3 much, but basically you can't can't
have a reference to a type that has already been dropped
(e.g. you can't allocate a `String` and then return a reference to it,
you'd have to return the owned `String`)
---
Predictable, safe memory management
without data races.
But why were `"fizzbuzz"` and `i.to_string()` incompatible?
Note:
We finally have enough to understand Owned vs slice types.
---
---
### String vs str
| Type | Description |
| ---- | -------------- |
| String | Heap-allocated, growable, UTF-8 string |
| &str | Reference to UTF-8 "string slice" (could point on heap, stack, or static memory) |
### String vs str
| Type | Description |
| ---- | -------------- |
| String | Heap-allocated, growable, UTF-8 string |
| &str | Reference to UTF-8 "string slice" (could point on heap, stack, or static memory) |
---
### Visualizing Strings

Note:
That's 3 stack variables containing pointers to a single
piece of memory on the heap
`"Hello world".to_string();` would actually create a `String` with `cap=size=11`
but I'm emphasizing that `cap` and `size` don't have to match
---
### String Conversions
`String` to `&str` is free via 'Deref' operator
```rust
let foo = "foo".to_string();
let bar = &*foo;
let baz: &str = &foo; // automatic deref
```
--
&str to String requires an allocation
```rust
let a: &str = "foo";
let b = foo.to_owned(); // via ToOwned trait
let c = foo.to_string(); // via Display/ToString traits
```
Note:
In practice, you rarely need `*` often because
auto deref works in combination with type inference
---
### Revisiting shared borrow
```rust
fn concat(a: &String, b: &String) -> String {
[a, b].concat()
}
fn main() {
let greet = "Hello ".to_string();
let alice = "Alice".to_string();
let bob = "Bob".to_string();
concat(&greet, &alice);
concat(&greet, &bob);
}
```
`&String` - were you going to read capacity?
Note:
`&String` is a pretty rare type to see since
having acceess to capacity but no ability to modify isn't very interesting
---
### Revisiting shared borrow
```rust
fn concat(a: &str, b: &str) -> String {
[a, b].concat()
}
fn main() {
let greet = "Hello ".to_string();
let alice = "Alice".to_string();
let bob = "Bob".to_string();
concat(&greet, &alice);
concat(&greet, &bob);
}
```
Use string slices (and auto `Deref`)
Note:
You can use `concat(&*greet, &*alice)`, but this is
where auto-deref and type inference work together nicely
But we can still do better..
---
### Revisiting shared borrow
```rust
fn concat(a: &str, b: &str) -> String {
[a, b].concat()
}
fn main() {
let greet = "Hello ";
let alice = "Alice";
let bob = "Bob";
concat(greet, alice);
concat(greet, bob);
}
```
Now it also works with string literals
Note:
Hence it is more flexible to accept `&str` than `&String`:
Of course, we still have to return a `String`,
which effective moves ownership of the concatenated string
back to the caller.
---

[Maybe, someday](https://internals.rust-lang.org/t/pre-rfc-allowing-string-literals-to-be-either-static-str-or-string-similar-to-numeric-literals/5029) string literals could just work as `String`.
Note:
Realistically, owned vs sliced types are pretty fundamental to Rust
---
| Owned Type | Sliced Type |
| ----- | ------ |
| String | str |
| CString | Cstr |
| OsString | OsStr |
| PathBuf | Path |
| Vec<T> | [T] |
Note:
`Vec<T>` and `[T]` are the fundamental types here,
e.g., `String` is a specialized wrapper around `Vec<u8>`
---
Is this borrow checker valuable?
Note:
So far this seems like ceremony, let's talk about why we tolerate this complexity:
---
### Iterate and append
Let's append to a list while iterating over it.
```scala
// Scala
var items = Array(0, 1, 2, 3);
for(item <- items) {
items.push(item);
}
```
What happens?
---

Runtime ConcurrentModificationException
---
### Iterate and append
Let's append to a list while iterating over it.
```rust
// Rust
let mut items = vec![0, 1, 2, 3];
for item in items {
items.push(item);
}
```
<p class="fragment">Compile error: use of moved value: `items`</p>
Note:
'for ... in items' allocates an iterator that takes ownership of items (to iterate over owned values)
---
### Iterate and append
Iterate over borrows instead?
```rust
// Rust
let mut items = vec![0, 1, 2, 3];
for item in &items {
items.push(*item);
}
```
<p class="fragment">error: cannot borrow `items` as mutable because it is also borrowed as immutable</p>
Note:
'for ... in &items' allocates an iterator that borrows items (to iterate over borrowed values)
---
### Concurrency
```rust
let mut account = Account::new();
let t = thread::spawn(move || {
let cached_balance = account.balance();
thread::sleep(Duration::from_secs(1));
account.set_balance(cached_balance + 1);
println!("balance: {}", account.balance());
});
t.join().unwrap();
```
> <!-- .element: class="fragment" --> balance: 1
[playground](https://is.gd/162lCt)
---
### Concurrency
```rust
let mut account = Account::new();
let threads: Vec<_> = (0..10).map(|_| {
thread::spawn(move || {
let cached_balance = account.balance();
thread::sleep(Duration::from_secs(1));
account.set_balance(cached_balance + 1);
println!("balance: {}", account.balance());
})
}).collect();
for t in threads {
t.join().unwrap();
}
```
<p class="fragment">Violates single owner and use after move</p>
[playground](https://is.gd/54xKVL)
Note:
Classic data race problem
Can't move `account` into 10 different threads
---
### Concurrency
```rust
let mut account = Account::new();
let threads: Vec<_> = (0..10).map(|_| {
let account = &account;
thread::spawn(move || {
let cached_balance = account.balance();
thread::sleep(Duration::from_secs(1));
account.set_balance(cached_balance + 1);
println!("balance: {}", account.balance());
})
}).collect();
for t in threads {
t.join().unwrap();
}
```
<p class="fragment">Error: cannot borrow immutable borrowed content `*account` as mutable</p>
[playground](https://is.gd/gotVF5)
Note:
While you can move 10 `&Account` references into 10 threads,
`set_balance` requires a mutable reference (`&mut Account`)
---
### Concurrency
```rust
let mut account = Account::new();
let threads: Vec<_> = (0..10).map(|_| {
let account = &mut account;
thread::spawn(move || {
let cached_balance = account.balance();
thread::sleep(Duration::from_secs(1));
account.set_balance(cached_balance + 1);
println!("balance: {}", account.balance());
})
}).collect();
for t in threads {
t.join().unwrap();
}
```
<p class="fragment">Violates "single mutable borrow" rule</p>
[playground](https://is.gd/gotVF5)
Note:
You can't have 10 `&mut Account` mutable references to `Account`
---
No data races!

---
### Concurrency options
- [Atomics](https://doc.rust-lang.org/std/sync/atomic/index.html), [Mutexes](https://doc.rust-lang.org/std/sync/struct.Mutex.html), [RwLocks](https://doc.rust-lang.org/std/sync/struct.RwLock.html)
- [MPSC Channels](https://doc.rust-lang.org/std/sync/mpsc/index.html)
- [Parallel work-stealing iterators](https://crates.io/crates/rayon)
- [Futures](https://crates.io/crates/futures) with [threadpools](https://crates.io/crates/futures-cpupool)
- etc..
Many concurrency abstractions that leverage the safety provided by `Send` and `Sync`
Note:
Hand-waving over solutions
The language provides `Send` and `Sync` trait markers that form the foundation
of an ecosystem with lots of safe concurrency abstractions
---
Fast, safe, productive -- Pick three.
How is Rust productive?

---
### Productive Rust
- Iterators ✓
- Closures ✓
- Futures ✓
- Macros
- Cargo
- Error handling
- Production
Note:
Want to cover a few more aspects of Rust that contribute to productivity
---
### Macros
```rust
#[derive(Deserialize, Serialize)]
struct Post {
author: String,
created_at: DateTime,
related: Vec<Post>
}
json!({
"name": "Alice",
"friends": ["John", "Carol", "Shakira"]
})
```
Boilerplate reduction
Note:
Macros make working with JSON almost as easy as in dynamic languages
without sacrificing any of the type safety.
---
### Algorithmia Macro
```rust
#[derive(Deserialize)]
struct Input { name: String }
#[entrypoint]
fn apply(input: Input) -> Result<String, Error> {
Ok(format!("Hello, {}", input.name))
}
```
Note:
Our own macro for specifying algorithm entrypoint - not yet stable
---
### Cargo
```rust
$ cargo new # stub out project
$ cargo test # run tests
$ cargo bench # run benchmarks
$ cargo doc # doc generation
$ cargo fmt # style correcting (rustfmt plugin)
$ cargo clippy # linting (plugin)
$ cargo install # install a rust binary
$ cargo publish # publish to crates.io
```
and the entire crates.io ecosystem
Note:
a lot of productivity from one tool
---
### Cargo test/bench
```rust
#[test]
fn test_div_by_3() {
assert_eq!(3/3, 1);
}
#[bench]
fn bench_push(b: &mut Bencher) {
let mut v = Vec::new();
b.iter(|| v.push(1));
}
```
---
### Great documentation
```rust
/// Builder method to configure the timeout in seconds
///
/// # Examples
///
/// ```no_run
/// # use algorithmia::Algorithmia;
/// let client = Algorithmia::client("sim123456");
/// client.algo("codeb34v3r/FindMinMax/0.1")
/// .timeout(3)
/// .pipe(vec![2,3,4])
/// .unwrap();
/// ```
pub fn timeout(&mut self, timeout: u32) -> &mut Algorithm {
```
[Docs](https://docs.rs/algorithmia/2.0.0/algorithmia/algo/struct.Algorithm.html#method.timeout) with examples that compile/run
Automatic doc hosting on docs.rs
---
### Error handling
```
// Scala
1. try { } catch {}
2. Try[T]
3. Either[A, B]
```

Note:
Really too bad that Scala has to carry Java's baggage on error handling
---
### Result
```rust
#[must_use]
enum Result<T, E> {
Ok(T),
Err(E),
}
```
```rust
let response = match algo.pipe(input) {
Ok(response) => response,
Err(err) => return Err(err.into())
}
```
Note:
Sorta like Scala's `Either[A, B]`
Notice the pattern of early returning on the `Err` case
---
### Try trait
try! macro or `?` suffix operator
```rust
let response = algo.pipe(input)?;
```
Chaining (crate)
```rust
let response = algo.pipe(input)
.chain_err(|| "Failed while calling anowell/pinky")?;
```
Note:
Rich error handling that stays out of your way
---
### Production

---

Note:
Type system and borrow checker extending what Scala prevents to include: NPEs and data races
Plus low barrier to entry to testing means testing picks up another good chunk
---
Fast, safe, productive -- Pick three.
---
What did I skip?
- Lifetimes (most are elided)
- Send + Sync
- Closure types: Fn, FnMut, FnOnce
- Associated types
- Pointer types: Box, Rc, Arc
- Interior mutability: Cell, RefCell, Mutex
(All great topics for an "Effective Rust" follow up)
{"metaMigratedAt":"2023-06-14T12:47:19.643Z","metaMigratedFrom":"Content","title":"Rust for Scalatians","breaks":true,"contributors":"[{\"id\":\"885543c6-0e40-4a1e-b3f1-3319d1e6a26b\",\"add\":0,\"del\":9}]"}