, 29 tweets, 5 min read Read on Twitter
Started learning Rust. First thing I noticed in tutorial: Variables are immutable (const) by default. This is a good start. Woohoo! :)
UTF-8 formatted strings! Awesome :)
Yay! No exceptions. Rust functions return error codes. You get a compiler warning if you forget to use the return value :)
A single standard build tool with dependency tracking. Downloads proper versions of used libraries (crates) and all their dependencies. C/C++ still has no de-facto standard for either. People copy paste code under random directories to their own repositories.
Rust has strong static type system. Compiler is also able to track resource ownership and threading issues. Big improvement over C++. Reduces need of unit tests. I wouldn’t call unit tests ”automatic” as you need to write + maintain them. Compiler is automatic, no added inertia.
Rust has sane integers. Nice names, instead of monsters like uint32_t. Bit depth is well defined (except usize and isize with are arch ptr sized). Overflow is 2s complement (not UB). Overflow is checked in debug mode (unless wrapping_ or saturating_ operations are used).
Rust has binary literals: 0b1111_0000_0101_0101. No need to decrypt hex numbers in your head to understand bit masks. You can also use underscores in numeric literals to improve readability.
I really liked the discussion of UB overflop loop counter optimization. Rust prefers unsigned ints (u32) fully sidestepping this C++ issue. My C++ code also uses unsigned for loop counters and array indexing. Using signed when negative is a bug is just asking for problems.
Integers don’t convert automatically to bool. Too bad C++ inherited this horrible property from C. Writing ”number != 0” is clear and type safe (get error in case a future refactoring changes assumptions).
If is an expression, no need for messy ”cond ? A : B” syntax. All brackets (code blocks) are expressions too. Value is the final expression inside the code block (no semicolor after it). Allows writing expressions with internal named temp variables (naming is good for reading).
Only one allowed mutable ref (or N immutable refs) mechanism is ingenious! Guarantees no aliasing (allows better codegen) and guarantees no race conditions. Heap is RAII with auto move (similar to unique_ptr), but compiler tracks vars & refs -> error for misuse. Huge improvement.
Rust has nice features such as enumerate for iter. Returns tuple of both index and element (item). Nice when you need to know the index of the loop iteration.

for (i, &item) in bytes.iter().enumerate() ...
Rust has good built-in tuples, and destructuring tuples has nice syntax. For example function returning tuple (multiple values) + destructuring the return value to a and b:

fn addsub(x: i32, y: i32) -> (i32, i32) {
(x + y, x - y)
}

let (a, b) = addsub(1, 2);
C++ lacks span type (ptr + size) until C++20. We’ve had our own span impl and it’s very nice. Rust is even better with built-in slice types (array & string). Rust borrow rules apply also to slices (compile error for stale slices)!

Syntax is nice:
&a[..] <-whole
&a[3..7] <-sub
I love how Rust separates struct data from methods. Easy to see the data layout and add more methods later without bloating the struct.

struct Rectangle {
width: u32,
height: u32,
}

impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
Rust doesn’t have null pointers. No need to add null safety checks or null asserts anywhere. For the special cases where some data may not exist, you use Option<T>. You can’t accidentally use Option<T> when it is None. You usually use ”match” to handle both Some and None cases.
In Rust ”match” is the equivalent of C++ switch case, but with broad pattern match capability. Compiler will give you error if you don’t handle all cases (some pattern in missing). This is nice. Also with Option<T> it means that None case needs to be handled to make code compile.
Rust generics are not duck typed like C++ templates. The compiler actually validates that your code compiles regardless of type T. You use trait bounds to define the ”interface” of T. If you do potentially illegal operations to type T the compiler gives you an error.
Trait bounds also offer a nice compile time interface mechanism. Compiler is able to track the actual type under the hood without needing to add any runtime code. Generates faster code than C++ interfaces with virtual functions. And type safety is better.
I really love how Rust forces the programmer to think about lifetimes, mutable data and races (mutable data accessible by other ref). Compiler validation gives programmer immediate feedback. Try using mutable globals, and you will rethink soon after fighting the compiler :)
C++ obj life time bugs, overflow bugs and thread races silently corrupt memory (likely crash later). Can’t reliably catch these with unit tests (incl must crash). Rust enforces life times, disallows races and overflow = panic. Unit tests are more reliable, esp failure tests.
Rust needs less (hand written) unit tests and debugging than C++, because Rust: A) is a strongly typed language, B) generics are not duck typed, C) obj/ref lifetime issues = compiler error, D) data races = compiler error. E) array overflows = panic...
Like C/C++, Rust uses stack for vars/structs by default. Built-in growing containers (Vec/String) internally alloc elem array from heap, because size is not known at compile time. Stack as default -> similar perf to C/C++. Safety is also better than most slow heap/GC based langs.
Rust iterators are safe (can’t be stale). Creating your own iters is simple, syntax is nice, iterators (filters, zips, maps, etc) can be combined seamlessly. In comparison C++20 ranges are messy, have obscenely slow debug perf and extreme compile time:

aras-p.info/blog/2018/12/2…
Rust does compile time borrow checking for variables, refs, String, Vec<T>, Box<T> (unique_ptr)... but Rc<T> (shared_ptr) refs are immutable to prevent races. RefCell<T> can be used inside Rc<T> to allow mutation with runtime borrow checking (panic if potential race).
Rust unsafe blocks maintain most safety mechanics (such as borrow checker), but allow for example raw pointers. This can be used to implement your own memory abstractions not supported by standard Rust. Unsafe block can be 100% safe if you validate the code correctness robustly.
Multithreading in Rust: Safer than C/C++ because borrow rules. Can’t store mutable refs around data structures and use them. But I am sad that there’s no built-in thread pool / task system. Each crate(lib) spawns their own threads, just like in C++. A big missed opportunity :(
Autovectorizarion works well in Rust. Borrow rules prove no-aliasing between mut references. Unfortunately Rust also does automatic bounds check for indexing. Compiler optimizations often remove this allowing vectorization, but sometimes non-obvious code changes are needed :(
Rust has antipatterns too. All these xxxCell wrappers allow internal mutability, breaking compile time guarantees. Used if you directly port OOP to Rust. As we C++ coders know OOP tends to degenerate to pointer soup and Rust really doesn’t like storing mutable refs around.
Missing some Tweet in this thread?
You can try to force a refresh.

Like this thread? Get email updates or save it to PDF!

Subscribe to Sebastian Aaltonen
Profile picture

Get real-time email alerts when new unrolls are available from this author!

This content may be removed anytime!

Twitter may remove this content at anytime, convert it as a PDF, save and print for later use!

Try unrolling a thread yourself!

how to unroll video

1) Follow Thread Reader App on Twitter so you can easily mention us!

2) Go to a Twitter thread (series of Tweets by the same owner) and mention us with a keyword "unroll" @threadreaderapp unroll

You can practice here first or read more on our help page!

Follow Us on Twitter!

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just three indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3.00/month or $30.00/year) and get exclusive features!

Become Premium

Too expensive? Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal Become our Patreon

Thank you for your support!