starting some notes on Rust

master
Oliver Kennedy 2024-05-01 23:52:52 -04:00
parent 090d456fa7
commit 069ccea66a
Signed by: okennedy
GPG Key ID: 3E5F9B3ABD3FDB60
1 changed files with 54 additions and 0 deletions

View File

@ -0,0 +1,54 @@
---
title: Rust...
author: Oliver Kennedy
---
I've been dinking around with Rust on a semi-serious basis for the past several months. I won't say that I've had enough time to form a fully educated opinion, but I do feel like I've gotten the general shape of the language... certainly enough to have at least a preliminary opinion. So... here goes nothing.
## Background
I come from the school of thought in system-building where I trust the compiler wholeheartedly. Refactoring a large codebase is far more pleasant in Scala than in Python... and the latter becomes far more pleasant with type annotations. I appreciate it when the compiler yells at me for changing a reference but not the referant. I'm willing to tolerate the compiler's pedantry, because ultimately it pays off in the long run.
In short, I'm the type of person Rust is aimed at.
## The Good
I've honestly had a ton of fun writing Rust. Not quite Ruby (or Frontier) levels of fun, but it's a very snappy language with good developer tooling. Rust's Struct/Trait model is, I think, much cleaner than normal object inheritance for most things that I typically use a class hierarchy for. And the borrow checker does a *really* good job of forcing me to think through memory management issues that I'd normally wave off.
#### Developer Tooling
Rust's developer tooling is flat out the cleanest that I've seen in a long time. The rust compiler does an excellent job of providing context for error messages. There's a lot of 'did you mean...' types of suggestions, and those are directly integrated into the LSP. Now, getting the LSP properly configured with sublime took a bit of time/effort, but once I did, everything just works: Easy access to docs, to suggestion implementations, and to error messages. Rust is incredibly pedantic about code artifacts, but the tooling does a *lot* to prevent that pedantry from becoming a barrier to authoring those artifacts.
#### Compile Times
My last major project relied on SBT. 'nuff said.
#### Documentation
`cargo doc` is up there with JavaDoc and ScalaDoc. Plus, `cargo doctest`'s ability to keep code snippets up-to-date, and the fact that `cargo doc` synthesizes documentation for all dependencies, makes the documentation ecosystem pretty nice.
## The Bad
#### Rust is Judgy
Most languages optimize for the typical use case. For example, managed languages like Java just put everything on the heap, and the user doesn't have to think about whether the size of an object can be predicted at compile time. Rust, meanwhile, puts that flexibility behind an explicit `Box` type (resp., `Rc`, `Arc`, etc...).
In short, Rust, the language, sits there, judgmentally eying you every time you try to do something that would just be the default in any other language.
It took a while to recognize this fact, and just start using `String`, `Box`, and the like without worrying about whether there's a 'cleaner' solution that can avoid them.
#### Compiler Magic is the Default
A lot of idiomatic Rust leverages non-obvious compiler trickery. As a simple example, let's say you want to `map` a function that can return a `Result`. If the map returns an `Err` on any of the inputs, we'd like to return an `Err` from the enclosing function, something that would normally be possible with the `?` operator.
The fact that this doesn't work makes sense when you realize that `Iterator` is lazy. However, it turns out that there *is* an idiomatic way to implement the same idea:
```
let foo: Iterator<Result<T>> = ...
let bar: Vec<T> = foo.collect()?
```
The `collect` method leverages Rust's compositional `From`/`FromIter` traits to factor the `Result` out of the collection when generating it.
All of the individual pieces are documented. The rules by which they are composed are obvious. However, the fact that this is the idiomatic way to get a `Result` out of a `map` is not at all obvious from any of the simple documentation.
In other languages (e.g., Scala, Ruby), there's a bevy of methods covering various specific, common use cases. These are far less flexible than `collect()`, but are far more discoverable.