Rust can Compete with Python

How enums, destructors, and aliasing rules
help us write clean code and catch bugs

  • blazing speed
  • bare metal
  • memory safety?
  • easy to write
  • easy to read
  • catch bugs
  • easy to write
  • easy to read?
  • catch bugs!

Enums

no more null pointer mistakes

$ python scream.py
HELLO WORLD

$ python scream.py /tmp/out
$ cat /tmp/out
HELLO WORLD
$ python scream.py
Traceback (most recent call last):
  File "/home/jacko/why_rust/scream.py", line 17, in <module>
    output = ScreamingOutput()
             ^^^^^^^^^^^^^^^^^
  File "/home/jacko/why_rust/scream.py", line 5, in __init__
    self.file = open(path, "w")
                ^^^^^^^^^^^^^^^
TypeError: expected str, bytes or os.PathLike object, not NoneType
$ python scream.py
Traceback (most recent call last):
  File "/home/jacko/why_rust/scream.py", line 19, in <module>
    output.write("hello world\n")
  File "/home/jacko/why_rust/scream.py", line 12, in write
    self.file.write(all_caps)
    ^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'write'
$ cargo run -q
HELLO WORLD

$ cargo run -q /tmp/out
$ cat /tmp/out
HELLO WORLD
$ cargo build -q
error[E0277]: the trait bound `Option<&str>: AsRef<Path>` is not satisfied
   --> src/main.rs:10:42
    |
10  |         let file = std::fs::File::create(maybe_path)?;
    |                    --------------------- ^^^^^^^^^^ the trait `AsRef<Path>`...
    |                    |
    |                    required by a bound introduced by this call
$ cargo build -q
error[E0609]: no field `file` on type `&mut ScreamingOutput`
  --> src/main.rs:21:14
   |
21 |         self.file.write_all(all_caps.as_bytes())
   |              ^^^^ unknown field
$ cargo build -q
error[E0063]: missing field `path` in initializer of `ScreamingOutput`
  --> src/main.rs:17:20
   |
17 |                 Ok(ScreamingOutput::File { file })
   |                    ^^^^^^^^^^^^^^^^^^^^^ missing `path`

error[E0004]: non-exhaustive patterns: `&mut ScreamingOutput::Stderr` not covered
  --> src/main.rs:25:15
   |
25 |         match self {
   |               ^^^^ pattern `&mut ScreamingOutput::Stderr` not covered

Destructors

no more resource leaks

$ cargo run -q
3 4
3 4
3 4
$ python look.py
3 4
3 4
3 4

$ pypy3 look.py
3 4
5 6
7 8

$ micropython look.py
3 4
5 6
7 8
$ python look.py
3 4
5 6
7 8

$ pypy3 look.py
3 4
5 6
7 8

$ micropython look.py
3 4
5 6
7 8
$ python scream_many.py /tmp/o1 /tmp/o2 /tmp/o3
$ cat /tmp/o1
HELLO WORLD
$ cat /tmp/o2
HELLO WORLD
$ cat /tmp/o3
HELLO WORLD
$ cargo run -q /tmp/o1 /tmp/o2 /tmp/o3
$ cat /tmp/o1
HELLO WORLD
$ cat /tmp/o2
HELLO WORLD
$ cat /tmp/o3
HELLO WORLD
$ cargo build -q
error[E0382]: borrow of moved value: `smallest_guard`
  --> src/lib.rs:13:6
   |
7  |     for i in 1..ints.len() {
   |     ---------------------- inside of this loop
...
10 |             next_guard = smallest_guard;
   |                          -------------- value moved here, in previous iteration of loop
...
13 |     *smallest_guard += 1;
   |      ^^^^^^^^^^^^^^ value borrowed here after move
$ cargo build -q
error[E0716]: temporary value dropped while borrowed
  --> src/lib.rs:8:42
   |
8  |         let next_guard: &mut i32 = &mut *ints[i].lock().unwrap();
   |                                          ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value...
...
12 |     }
   |     - temporary value is freed at the end of this statement
13 |     *smallest_guard += 1;
   |     -------------------- borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

Aliasing Rules

no more "spooky action at a distance"

$ python food.py
alice: ['donuts', 'apples', 'bananas']
  bob: ['donuts', 'apples', 'bananas']
$ python food.py
alice: ['donuts', 'apples']
  bob: ['donuts', 'bananas']
$ cargo run -q
["donuts", "apples"]
["donuts", "bananas"]
$ python circular.py
▯
Killed
$ python capture.py
9
9
9
9
9
9
9
9
9
9
$ cargo run -q
0
1
2
3
4
5
6
7
8
9
$ cargo build -q
error[E0594]: cannot assign to `sum`, as it is a captured variable in a `Fn` closure
 --> src/main.rs:6:9
  |
6 |         sum += i;
  |         ^^^^^^^^ cannot assign
  • no mutation
  • no "spooky action at a distance"
  • catch bugs
  • no mutation aliasing XOR mutation
  • no "spooky action at a distance"
  • catch bugs

Standalone Scripts

watch this space

$ ./echo.py hello world
HELLO WORLD
$ ./echo.py hello world -n
HELLO WORLD$ ▯
$ cargo install rust-script

$ ./echo.rs hello world
HELLO WORLD
$ ./echo.rs hello world -n
HELLO WORLD$ ▯
$ ./echo.rs hello world
HELLO WORLD
$ ./echo.rs hello world -n
HELLO WORLD$ ▯