|
|
|
|
|
by steveklabnik
430 days ago
|
|
The history here is very interesting, Rust went through a bunch of design iteration early, and then it just kinda sat around for a long time, and then made other choices that made modifying earlier choices harder. And then we did manage to have some significant change (for the good) in Rust 2018. Rust's users find the module system even more difficult than the borrow checker. I've tried to figure out why, and figure out how to explain it better, for years now. Never really cracked that nut. The modules chapter of TRPL is historically the least liked, even though I re-wrote it many times. I wonder if they've tried again lately, I should look into that. > Another example is the trait object (dyn Trait), which allows the client of a trait to decide whether dynamic dispatch is necessary, instead of baking it into the specification of the type with virtual functions. Here I'd disagree: this is separating the two features cleanly. Baking it into the type means you only get one choice. This is also how you can implement traits on foreign types so easily, which matters a lot. |
|
I'm surprised the module system creates controversy. It's a bit confusing to get one's head around at first, especially when traits are involved, but the visibility rules make a ton of sense. It quite cleanly solves the problem of how submodules should interact with visibility. I've started using the Rust conventions in my Python projects.
I have only two criticisms:
First, the ergonomics aren't quite there when you do want an object-oriented approach (a "module-struct"), which is maybe the more common usecase. However, I don't know if this is a solvable design problem, so I prefer the tradeoff Rust made.
Second, and perhaps a weaker criticism, the pub visibility qualifiers like pub(crate) seems extraneous when re-exports like pub use exist. I appreciate maybe these are necessary for ergonomics, but it does complicate the design.
There is one other piece of historical Rust design I am curious about, which is the choice to include stack unwinding in thread panics. It seems at odds with the systems programming principle usecase for Rust. But I don't understand the design problem well enough to have an opinion.