After a year and a half of learning, it was interesting to write down what were the difficulties that just are gone, what remains hard, and what was easy.
Note: I still write Rust 90% in spare time, not at work, and have kids, so my time spans are longer.
Facepalms:
- Half a year of experience I figured out that some CamelCase names in docs weren't types, but traits. I had read the book, saw a lecture on traits, but still they were in passive background knowledge.
- A week ago, re-visiting some crates, I checked out their dependencies and saw "serde", which implied they were serde-compatible, while I had implemented this myself.
- I would see some methods of structs in the docs, try using them -- and compiler said they weren't there. They came with traits. Took several times start paying attention and read above the method in the docs.
Hard things not noticed anymore:
-
Borrowing. The examples in the book are so primitive, they don't make you feel how it works.
let x = vec![1, 2, 3]; let y = x; println("{:?}", x);
But it actually bites you later, when you realize that you can't borrow in a cycle. I ran into this about 8 months ago, and only then started to fully feel where things should be borrowed or not.
-
Crates features. It was very irritating when you write a method call, or import something, and it's not there.
-
It was impossible to tell where macros were imported from.
-
Macro/function distinction.
-
Type/trait distinction. It may be just my case, but it's easy to confuse them when you're beginner. Only after I banged my head against R-Star crate, I "got it".
-
Error handling mixed with iteration. You're irritated that it's that complicated, that you can't just implement "next". But then just get used to it.
-
Generics with trait bounds.
-
Iterations over vectors or hash(map|set) -- when I was almost a year into Rust, this was the biggest annoyment.
-
Piles of match clauses. At the beginning, match was the irritating duty to process all the options & results. Right now it's a safe fallback when methods (
.map()
,.ok...()
) orlet-else
don't work.
Unexpectedly Easy Things
- Getting anything to work. After ~1 month from the very beginning, I took an algorithmically complex task that I earlier had written in Python, and re-wrote it, using just owned vars and
clone()
when needed. And it worked, a lot faster than Python. - Parallelization and crossbeam. Was very straightforward, the syntax of closures is elegant too.
- Macros. It took several hours to read the small book on macros. But after you get it going, learning further in details is easy. A geat investment of effort that saves you a lot of code later.
- Testing. Initially you learn the structure like it were a boilerplate, and that's it -- you got it going.
- How to organize modules and import items. Turned out, you need 2-3 examples to get used to this.
Still Not Done
- Async. Just purposefully avoided it for a while, to have 1 layer of complexity less.
- Reading various file formats from different crates in one general-purpose API struct. Tried this 8 months ago and completely failed to marry owning and borrowing readers.
[ADDED on 10.9.23] Still Irritates
When a method is &mut self
, and you call another method, blocked are the entire object and everyone that borrows it partially or entirely. You must out-refactor the call into methods of nested members or standalone functions:
impl<'a> MyRouter<'a> {
pub fn method1(&'a mut self, ...) {
self.method2(...) // THIS CALL causes entire structure to be borrowed
// do something useful too on self
let result = self.property1...
}
pub fn method2(&'a mut self, ...) {
...
}
}
Feelings
The challenge of getting just any code to work was easy. It was very hard actually to get anything useful done -- like take a library and use it.
After 3-6 months, it was still hard to understand the terse docs of libraries, or make something compatible with other libraries (fit the trait boundaries which I didn't understand at all).
At 1 year, it was hard to iterate over structs or use a lib in a complex way.
At 1.5 years, biggest challenges are to architect and plan ahead.
Thanks to all who helped me here, you came at very critical, bottleneck moments.