I have heard this reaction from others before. One of the Rust expert friends I consulted with told me "I'm not convinced you're not trying to write Haskell-style code in Rust;" I told him the patterns I was struggling with were both trivial and common in Java.
The things I found quite difficult or impossible in Rust were to me pretty basic patterns for modularity and removing duplication that it's really shocking that these complaints are not more common.
I currently have but two hypotheses for why.
First, the second problem I mentioned only comes from using tokio, which causes your top-level program to secretly be using a defunctionalized continuation data type, derived from where exactly in other files you put your await's, that might not be Send. If you're not using tokio, you won't experience that issue.
Second...I was kinda told to just give up on deduplication and have lots of copy+pasted code. This raises the very uncomfortable hypothesis that Rust afficionados are some combination of people who came to Rust early and never learned traditional software design and don't know what they're missing, and people who were raised on traditional good software engineering but then got hit with Rust's metaphorical baseball bat of lack-of-modularity over and over until they got used to being hit with a baseball bat as a normal pain of life.
I don't like either of these explanations (esp. with tokio seeming quite dominant), so I'm awaiting an explanation that makes more sense. https://xkcd.com/3210/
> difficult or impossible in Rust were to me pretty basic patterns for modularity
Many things are plainly not permitted, either because the borrow-checker isn't clever enough, or the pattern is unsafe (without garbage collection and so on).
Many functional/Haskell patterns simply can not be translated directly to Rust.
> Rust afficionados are some combination of people who came to Rust early and never learned traditional software design and don't know what they're missing
This is definitely not the case and is unnecessarily insulting.
The truth is that some things are harder in Rust but a) often those things are best avoided anyway (e.g. callbacks), and b) it's worth the trade-off because of the other good things it allows.
Surely as a Haskell user of all things you must understand that sometimes making things harder is worth the trade-off. Yeay everything is pure! Great for many reasons. Now how do I add logging to this deeply nested function?
The most confusing thing that can happen with something like tokio is the failure-at-distance you can get from writing a non-Send future somewhere in the depths of a call stack and then having to figure out why your top-level spawn isn’t working. There’s a non-default lint I highly recommend turning on when working with tokio: clippy::future_not_send. Forces all your futures to be Send, unless you opt out, which really helps keep the reasoning local when you run into errors.
FWIW I write primarily rust, and I do not agree with the advice given in your second point, so I’d take it with a grain of salt were I you.