4 ms·
> Zig is dramatically simpler than rust... Most of this difference is not related to lifetimes. I've been thinking about this recently: the borrow checker gets
by skohan 6y ago
> Zig is dramatically simpler than rust... Most of this difference is not related to lifetimes.
I've been thinking about this recently: the borrow checker gets a lot of attention, but I think the majority of the learning curve of rust is actually due to "unforced errors" in the language UX which have nothing to do with the language's core USP's.
For instance, the module system just seems needlessly complex. Like you need a mod.rs file in each subdirectory, and the main.rs/lib.rs serves as the module file for the src directory, it took me like a day to figure out what exactly the rules are, and I can't understand why there can't be sensible defaults to define modules based on the file system structure alone.
- steveklabnik 6y agomod.rs isn't needed anymore; back in 2018 we fixed that: https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-clarity.html#no-more-modrs https://doc.rust-lang.org/edition-guide/rust-2018/module-sys... > why there can't be sensible defaults to define modules based on the file system structure alone. There could be, and in fact, I personally advocated for them. But there was significant community pushback; it turns out many people like to "comment out" entire modules when doing big refactorings. There are also some interesting edge cases, for example, you can use a #[path] attribute on a module to change what the path to the file is; without a mod statement, it's not clear where that would go.
- skohan 6y ago> mod.rs isn't needed anymore; So I'm not sure I'm a fan of this either. So now for a module with sub-modules, part of it is defined in the top-level directory, and part of it is defined in a sub-directory. Now if I want to move a module from one directory to another, I have to move two items in the file system. > it turns out many people like to "comment out" entire modules when doing big refactorings. It seems to me it would be much better to have an "exclude.rs" file or something to opt out of a sensible default rather than forcing extra work in the common case just to support the common case. Or else you could still allow a mod.rs if you want to be explicit about your module contents, and just assume it includes everything in the directory if it's missing > There are also some interesting edge cases, for example, you can use a #[path] attribute on a module to change what the path to the file is; without a mod statement, it's not clear where that would go. Again, I don't think a design should be optimized to support interesting edge cases. It should make the common case as simple as possible. If edge cases need to be supported, I'm sure a solution can be found
- steveklabnik 6y agoI am not saying you should love the module system, just trying to provide some context. (Everyone seems to love and/or hate the module system for various, opposing reasons. It is a great mystery to me. Explaining the module system is like, kind of my white whale.)
- skohan 6y agoYeah I just think it’s a trend with rust where when a new user complains about a beginner-unfriendly feature, often they are met with an explanation of some esoteric benefit. I just wonder how much of that is as-hoc reasoning, and it doesn’t seem like it’s a particularly good sign.
- steveklabnik 6y agoI thought this way too until I saw a bunch of people praising how easy the module system was because they felt it was very similar to Python's. I don't know Python well so I can't speak to it. My working theory: each language does modules in a different way. People think "Oh, a module system, I know this" and then run into issues when it works differently than their language. Just a theory though, I have not been able to validate it, or to figure out an explanation that works for all people. And yes, more generally: as Rust continues to grow, more and more people will hit more and more edge cases, and they cannot always be fixed, thanks to being backwards compatible. That's just how things are as they grow up. More and more users also brings more and more use cases. It's a feedback loop.
- blandflakes 6y agoOof I dunno I'd categorize the Python module system as "makes no sense and I can never get it right" - I'm sure it's consistent but it doesn't feel approachable to me.
- kbenson 6y agoHonestly, that idea makes a lot of sense to me, and personally, I find the module system pretty normal seeming for a modern language. I'm used to Perl and CPAN, and it's pretty similar to that, except for the option to use dir/mod.rs, which honestly seems like it's kinda nice for keeping a module contained nicely for those that want to do it that way. Now I'm more interested in what the complaints about it are, and specifically what they're in comparison to. Either they're not used to using so many modules in a method such as this, or they're honestly expecting some better system for their use case that I'm unaware of, or a little of both, so my curiosity is piqued.
- tym0 6y agoProof that it is still quite confusing, I tried to remove the mod.rs in my project similarly to that example but now I don't understand how to access these modules. With the mod.rs in place I can do this in my main.rs: pub mod foo and then in a different file do: use crate::foo::bar::* But without the mod.rs it doesn't work and I don't know what the correct incantation is... Edit: Nevermind, I thought this update meant you could remove the mod.rs completely but this is about declaring foo.rs as a file outside of the foo folder. So you can't just do this like I was hoping: . ├── lib.rs └── foo/ └── bar.rs
- steveklabnik 6y agoIf you have foo/mod.rs you can now use foo.rs that's it. Contents of the file are exactly the same. Nothing to do with changing the mod line, or the use line. Those all stay the same. It's about files in the filesystem.
- tym0 6y agoIt's my bad I thought I could remove the mod.rs that just redeclares the submodules in my case.
- JoshTriplett 6y agoIf you'd like to have two modules, `main.rs` and `foo/bar.rs`, and you want the latter to be `foo::bar`, you can do this in `main.rs` to avoid needing a foo.rs that just does `mod bar;`: mod foo { mod bar; }
- tym0 6y agoI was just following the example from the docs, my use case is more like this: . ├── lib.rs └── foo/ └── mod.rs └── one.rs └── two.rs └── three.rs └── four.rs └── bar/ └── mod.rs └── one.rs └── two.rs └── three.rs └── four.rs Which feels to me like a very common folder structure in other languages.
- dom96 6y agoNice to hear that there has been some progress here. I recall trying Rust 4+ years ago and my experience with the module system really left a lot to be desired from someone most experienced with Python's module system.
- aszen 6y agoI was confused by rusts module system too, after having used es6 modules in js and python but then I realised the module systems couldn't be compared. In dynamic langauges like python or js u can individually use modules but in rust everything has to link back to either main or lib if I'm not mistaken. This was what enabled me to get rid of preconceived notions of how a module system should work.
- jamii 6y agoI don't think this is fair. There is definitely some unforced error, but a lot of the complexity is shared with other languages in it's heritage eg the trait system is no more complicated than haskell's typeclasses, and haskell also has macro systems on top of that. Also the simplicity in zig comes from a major innovation in controlled partial evaluation, which we've yet to really explore the consequences of. Rust had already made a major innovation in the form of the lifetime system. It needed to figure out all the implications of that so it made sense to draw from existing designs where possible for the rest of the language rather than piling on more new and untested ideas.
- hawkice 6y agoI mean, if you're saying Rust isn't any more complicated than _Haskell_, I mean, you're not wrong... [edit: I'd like to clarify that the Rust team has tons of talent and good ideas and can probably find a goal more ambitious than this. Haskell has a definite aesthetic, and it fits some people/projects better than anything else. That aesthetic does not include simplicity.]
- brundolf 6y agoAnother large category of language UX issues is, ironically, the sugar that gets applied to the syntax. The classic example, but far from the only example, is auto-dereferencing: https://stackoverflow.com/questions/28519997/what-are-rusts-exact-auto-dereferencing-rules https://stackoverflow.com/questions/28519997/what-are-rusts-... Rust has many syntax features that can be described as "Rust takes some common pattern that's onerous to write and read, and automatically infers it for you under certain conditions". On its face this seems like a strict improvement: you can still be explicit if you want/need to, or you can skip some boilerplate in certain cases. But the problem comes when you're trying to learn about the language. Because these UX "optimizations" are layered on fairly arbitrarily - not unlike actual compiler optimizations - they sometimes create a very confusing landscape to try and form a mental model around, in the same way that compiler optimizations can make it hard to understand what will and won't make something faster. This often plays out as: 1) You have some clean piece of code that does what you want 2) You add something innocuous to it 3) It no longer fits the sugar-pattern that the compiler was silently invoking underneath 4) You now have several sprawling errors because you're expected to be explicit about something you didn't have to be before An inexperienced Rust programmer would (reasonably) assume those errors were caused by the thing that was added, and start trying to figure out what's wrong with it. But that's a red herring. The real issue, which is not indicated, is that a sugar-pattern was bailed out of. I'm glad these shortcuts exist in some capacity: Rust is a complicated and fairly verbose language, and they make it less so. But I think they seriously damage the learning experience and early impressions that people form of the language. I've been using it for years and I still discover new quirks with this stuff that I didn't know about, and incorrect assumptions I had about the language itself that were driven by these mysterious mechanisms. What if rustc had a "no-sugar" mode that people could use until they've gotten a handle on what's really going on? What if the language server somehow indicated inline when sugaring was being invoked? Edit: A different way of phrasing the problem is that debugging is like navigating a landscape: there's a locality to it. "Did this change bring me closer to my goal, or further away from it? If closer, I'm probably on the right track, if further, probably the wrong one." Most languages mostly adhere to this idea of contiguous space-navigation. But these patterns in Rust are like constructing a maze across the landscape; there are cul-de-sacs and roundabout pathways you have to follow to get where you're going. There's also inconsistency about a given subject: you look at one piece of the terrain from a different angle, and it changes. It's hard to form a coherent, generalized mental map because base truth is relative based on what direction you're coming at it from. So over time instead of learning 2N concepts (lifetimes, references, iterators, boxes) you learn an NxN matrix of concepts (using references with boxes, using references with iterators, using lifetimes with iterators, etc...), because they interact with each other in unpredictable ways. This may just deserve a whole blog post :)