High-Level Rust: Getting 80% of the Benefits with 20% of the Pain
20 points by deejayy
20 points by deejayy
While I don't have the patience to dig through Rust discourse for sources, I feel like this (oft-overlooked) suggestion has been around for a while now. Albeit in the form of training wheels to writing Proper Rust.
I largely agree with the author. I will controversially claim that the benefit of using Rust (the programming language in isolation --- there is much to be praise to be said for the ecosystem) to the average developer is not the borrow checker, but its type system. The world was not ready for Haskell, alas.
As an "average developer" who moved from Python, it wasn't difficulty picking up Haskell that turned me away from it, but lack of fitness for purpose, risk of space leaks, and the perception that the ecosystem is more willing to break API.
Rust has PyO3, wasm-bindgen, and so on, while Haskell has a garbage collector to get into territorial fights with other garbage collectors, and I perceive Rust as having "all the bits of Haskell before you go too far down the curve of diminishing returns".
(Seriously. Being able to write code once and then incorporate it into other projects in other languages without needing to RPC across multiple processes or code in C or C++ is Rust's killer use-case for me.)
Proper Rust
I'm not sure this style of Rust is proper or even good.
I suspect part of what they mean by "proper" is "not leaning on eager use of Rc/Arc or equivalent to bypass having to reason through ownership and borrowing"... and that very much is proper and good in the sense that...
Here are the necessary components of Rust to make imperative programming work as a paradigm. Shockling few other production-ready imperative languages have the first of these, and none of them have the others at all (at least, none have them implemented correctly; C++ has unsafe analogs). Unsurprisingly, the common names for these concepts are all opaque nonsense:
- “Algebraic data types”: Having both “product types” (in Rust structs) and “sum types” (in Rust enums) is crucial. The language must not have null, it must instead use an Option wrapper. It must have strong pattern matching and destructuring facilities, and never insert implicit crashing branches.
- Resource acquisition is initialization: Objects should manage conceptual resources like file descriptors and sockets, and have destructors which clean up resource state when the object goes out of scope. It should be trivial to be confident the destructor will run when the object goes out of scope. This necesitates most of ownership, moving, and borrowing.
- Aliasable XOR mutable: The default should be that values can be mutated only if they are not aliased, and there should be no way to introduce unsynchronized aliased mutation. However, the language should support mutating values. The only way to get this is the rest of ownership and borrowing, the distinction between borrows and mutable borrows and the aliasing rules between them.
In other words, the core, commonly identified “hard part” of Rust - ownership and borrowing - is essentially applicable for any attempt to make checking the correctness of an imperative program tractable. So trying to get rid of it would be missing the real insight of Rust, and not building on the foundations Rust has laid out.
-- https://boats.gitlab.io/blog/post/notes-on-a-smaller-rust/
I like this idea. Where I'd really like to see it go, is a "soft rust" language that reads closer to Python or Typescript, and compiles to this high-level Rust, with the ability to write real Rust inline, for seamless interfacing between the two approaches.
That's probably where OCaml is heading with OxCaml and effects. Simple and correct, yet you can squeeze performance if needed.
Haskell could have filled that niche with a reduction in accidental complexity. There's a subset of Haskell that is quite simple, useful, and elegant.
I really, really wanted to like OCaml, but it's just not offering me enough above and beyond everything else out there. There are now lots of GC languages offering at least some lip-service toward "types". And OCaml has some significant warts (try writing a "print" function that can handle all your types and enjoy the madness or experience the joy of specific add functions for integer vs float).
And the OCaml ecosystem is ... something.
OxCaml definitely does not read close to Python. It could be a more interesting try to compile Gleam to OCaml?
I think it’s going to be more successful to look at a higher level abstraction on top of rust like objective c did for c. Any c compiles fine in objective c, but you can leverage objective c’s new abstractions to break up those large programs along squishy boundaries.
Rust is perfect, except for the productivity
Cons:
- Learning curve - ownership, borrowing, lifetimes, async runtimes and gotchas
As someone who didn't find the learning curve difficult, even coming from Python back with Rust v1.0 and the first version of The Book in 2015, I think this is highly subjective.
Granted, my use of async is pretty superficial stuff on top of actix-web and I'd already developed a very Rust-friendly approach to structuring my Python programs to improve maintainability and testability.
- Lower velocity - vs high level languages
For me, the three big hold-ups for making Rust not only be more productive than Python for me, but feel at least as productive are:
mold, and 50% of the approximately 2 seconds an incremental rebuild takes is spent with one core pegged in the rustc frontend, thus should be mitigated by efforts to parallelize the frontend, the Cranelift codegen, and, if the effort wakes up again, efforts to relink, don't rebuild.)std has UX flaws and I'm missing common crates.Aside from those, I find Rust to be more productive than Python when you count how much time I spend in Python doing QA and debugging after banging out the initial proof of concept but before turning it loose on my actual files.
...and, from what I've seen since 2015 during the period when Reddit had RSS feeds, I'm not the only one who finds Rust to match or exceed development velocity in other languages.
Not a fit:
- Hot paths
Honestly, the more I use Rust, the more I wind up turning cargo flamegraph on everything.
(Not bad for someone who used to be so Python-headed that I'd dismiss ideas involving writing CPU-bound code myself before it even reached conscious attention.)
Rust to be more productive than Python
Rust is more productive than Python if you know exactly what you're building. For exploratory programming (which is pretty much where all the interesting stuff happens), Python is much much more productive.
I disagree on the degree of "exactly" you seem to be implying with your "which is pretty much where all the interesting stuff happens".
Yes, Python is likely to be faster for a project like my on-hold effort to develop a parser which can heuristically infer a title-suitable program name from a file/folder name, which requires a lot of fiddling with heuristics to figure out what will maximize the accuracy score on a corpus of known filenames→title mappings.
However, outside that extreme, I'd argue there's plenty of interesting stuff that easily falls into the realm of what I find faster in Rust than in Python when not being afraid to .clone() and so on, thanks to things like Serde being, in my experience, friendlier to exploratory programming than things like Pydantic or hand-rolling the equivalent code.
To some extent, it's in the tooling. The power of rustdoc, a strong type system that code can't skimp on feeding information into, and good uses of derive() macros with sufficient documentation is not to be underestimated.
That depends on what is it you are exploring. For me exploring is usually about figuring out types, not implementation details.
There's an entire application that still needs to be built on top of those types. That's often (conveniently) forgotten.
I will, in fact, claim that the difference between a bad programmer and a good one is whether he considers his code or his data structures more important. Bad programmers worry about the code. Good programmers worry about data structures and their relationships.
— Linus Torvalds
Show me your flowcharts and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowcharts; they'll be obvious.
— Fred Brooks
I have to admit, I kinda got record scratch moment at the DDD/DI part. That's common in today's functional-purism-sphere, too?
In my opinion, the best Rust codebases are already doing a great job adhering the recommendations from the article.
I don't know TypeScript enough, but I wonder if the author knows Python enough. Python typing story is quite nice lately. Most packages I use have typing information and I add a lot in my own code and correct a lot of issues that way. With structural match I finally can tell that I like Python very much. Performance story is indeed bad for Python. I am yet to use some kind of compiler/transpiler for my Python code, but I know that those options are available.
Of course Rust offers a different set of advantages/disadvantages.
Honestly this definition of expressive types is what, 20% of the Rust type system?
https://hamy.xyz/blog/2026-01_missing-programming-language#my-definition-of-expressive-types
High level Rust as described there is great, but it gets annoying any time you have to interface with some absurdly type/macro heavy library. If you're doing everything with LLMs, you may be able to sidestep this entirely and generate all the code from scratch.