That's where the learning curve started to get very steep for me.
You're not exactly the target market if you're writing python.
That Rust program you spent a day golfing to make beautiful use of iterators? Your friend wrote it in go in 15 minutes.
They also get static types, good dependency management, and singe executable deployment. Cython + mypy basically gives you that with Python.
Rust gives you a lot of ways to express yourself, but now that I am quite familiar with it, I can write Rust just as quickly as I can write Python or Go.
You can spend all day code golfing in any language.
For production contexts, we take more care to optimize for performance.
Arcs can be expensive, but once you’ve got the sense for lifetimes, they aren’t that hard to avoid.
Does it? It's like using shared_pointer, which some C++ codebases do everywhere for memory safety.
And some languages like Swift even do it by default.
Actually I left Rust and moved to Go although I love Rust much more because every struct ended up sprinkled with Arc.
FWIW I write Rust professionally now, and in our entire codebase we have maybe four or five data structures containing Arcs, specifically where objects are spawned as Futures on other threads.
Tokei says that we've got ~76k lines of Rust.
I used ripgrep to find occurrences of `Arc::` (to exclude type signatures and only find constructors) and then just gave it a quick manual look to exclude tests and benchmarks.
This leaves me with:
- one data structure that contains general application state and contextual information, which is created at application startup and shared across all tasks/threads
- one data structure for accumulating results that is shared across threads. This is an Arc<Mutex<u64, SomeEnum>>. The less common of the two enum variants is an Arc<BTreeMap>.
- one data structure used to pass a database connection in to a context where it'll be used to spawn async tasks
All told, we've only got four places in production paths where we construct a new Arc. One of those four is only called once in the life cycle of the application, while the others are called for any given invocation.
Care to recount what some of those misunderstandings were? I’m casually interested in Rust but only really observe from afar, since most of my day job is Swift. Don’t get me wrong, Swift has some odd limitations around protocols (closest equivalent to traits) that may be similar, but I’m curious to see what some common pitfalls may be with Rust traits.
I think it can be quite confusing initially how traits are both the unit of compile-time generics AND can be used for dynamic dispatch at runtime, given that there are separate rules about what can be used in which context.