5 ms·
What's the rationale for making files modules? Modules being namespaces, and namespaces being collections, it's not desirable to have a file being a (large) col
by pizza234 6y ago
What's the rationale for making files modules? Modules being namespaces, and namespaces being collections, it's not desirable to have a file being a (large) collection of code.
The feature that gives mixed feelings to the author (Re-exporting Imports) is indeed a workaround to this; since each file would externally generate one extra namespacing level, one reexports data structures in order to remove that level.
This is odd, as it's essentially boilerplate by design. Am I missing something?
- q3k 6y agoYeah, Rust's module system is also something I find very difficult to work with after working with Go. Any time you end up with a lot of code/logic/types in a single module you have to either deal with a multi-kloc file, or try to shoehorn it into further namespacing, even if it doesn't make sense. Both options are annoying to both read and write, and I wish I could just split up complex modules into multiple files without the extra namespacing.
- eptcyka 6y ago`mod shard; use shard::*;` doesn't seem too bad IMO. Unlike Go, the compiler won't just parse all files in the source directory, but rather it'll build a tree of modules from the entry point and parse those.
- q3k 6y agoYes, but then you introduce new scope. If shard1::Foo depends on shard2::Foo, you then end up with a murder of import statements at the top of every file. Not to mention, splitting along the lines of inter-dependency does not necessarily correlate with splitting by readability/relevancy.
- eptcyka 6y agoYes, but this downside is not unique to Rust. It certainly exists in Java, Typescript, Kotlin and others. I'd much rather be certain where an imported type is coming from rather than having to search through all of the files. Of course, this is what an IDE should do, but even still, it saves the IDE's time. I fear long files less than the go module system.
- littlestymaar 6y ago> Yes, but then you introduce new scope. If shard1::Foo depends on shard2::Foo, you then end up with a murder of import statements at the top of every file. Wait, what? Import statements are recursive in Rust, if shard1::Foo depends on shard2::Foo, you just need to manually import shard1::Foo.
- q3k 6y agoOf course. I mean importing shard2 within shard1. You can always `use super::*` but, ugh. And with most imports in Rust being unqualified (that seems to be the idiomatic approach), I then find it difficult to differentiate types that are from a sibling, internal module from types that are important from external crates. Especially at site of use of the type. Go's “local package names unqualified, everything else qualified” makes it much easier for me to read code without first having to parse all the import statements.
- Nullabillity 6y agoAs someone who mostly writes Rust these days, I find it really nice that I can always (as long as I don't use wildcard imports) use intra-file search to find either the definition or the file that contains the definition. Whenever I have to touch Go code (for reading and debugging) it's really annoying to just end up going "okay, I've got the folder, now what". Or, even more annoyingly, I've just got an interface and no clue about where to find the implementation. Of course, that wouldn't be so bad if pls was actually usable and helpful. But at the moment it would be hard to call it either of those things.
- LandR 6y agoDont most IDEs solve this. I do C# for my day job, if I have an interface I put cursor on it and hit a key and go to the definition, whatever file it's in. Another key will cycle the usages of the interface. This has been solved for years. More recently we've had things like Peek as well.
- littlestymaar 6y ago> Dont most IDEs solve this. Working IDEs do, but Go's PLS doesn't, and that's part of the GP's complaints: > Of course, that wouldn't be so bad if pls was actually usable and helpful. But at the moment it would be hard to call it either of those things.
- domano 6y agoThat is why i made the switch to Goland, even if i switched to VSCode from the Jetbrains Products before. Sure its a bit sluggish in comparison (well only when indexing really), but finally i do not have to worry about not finding something or import-completion breaking every 2 weeks etc. Just switch, you will stop thinking about your setup and just work instead.
- ReactiveJelly 6y agoBut if you have to rely on an IDE, it's hard to innovate in language design. Rust does have rust-analyzer, but it uses piles of RAM and often just doesn't work. I'll periodically try it for a few days and give up on it again. Maybe it's a bug in Kate's LSP support.
- chrismorgan 6y agoOne important technical reason is that you need to be able to add attributes to modules, like these examples: // Only compile this code if the “foo” Cargo feature is enabled. #[cfg(feature = "foo")] pub mod foo; // Platform-specific stuff, as with std::os. pub mod os { #[cfg(windows)] pub mod windows; #[cfg(unix)] pub mod unix; } // You can even choose which file to load the module from. #[cfg_attr(windows, path = "windows/mod.rs")] #[cfg_attr(unix, path = "unix/mod.rs")] #[cfg_attr(not(any(windows, unix)), path = "null/mod.rs")] mod platform_impl; So they need to at least be addressable.
- pizza234 6y agoInteresting. This made me realize that a possible reason why some find the modules concept confusing is that it conflates the concepts of inclusion and namespacing. edit: corrected typo
- KMag 6y agoHas the joke flown over my head, or have you conflated the words "conflate" (confuse) and "conflagrate" (burn)? Edit: I honestly think I might have just misunderstood some play on words you're trying to make. It would be something around setting namespaces and inclusion on fire, but none of the combinations I've tried in my head work out quite right.
- pizza234 6y agoD'oh! Thanks for the correction; there was no pun - I just intended that it combined the two concepts :-)
- kd5bjo 6y agoRust modules are primarily about namespacing, which is why the book only mentions the inclusion behavior in one short section at the end of the relevant chapter. For a non-namespacing include, there’s the `include!` macro.
- 6y ago
- pornel 6y agoFiles aren't modules. Modules are defined in the source code the same way as any named "item" in the language, like structs and functions. The only difference is that content of `{}` following the definition of a module can be read from another file. You can have a module without a file: mod foo { fn function_in_foo() {} mod bar { // this is crate::foo::bar module! } } but if you omit {}: mod foo; Rust will look for `foo.rs` to drop its content where the {} should have been. But namespacing is governed by `mod` declarations alone, not files.
- ReactiveJelly 6y agoThat's not how I understand it. Every file is a module, but not every module is a file. If `mod foo;` is the only way to make sure a file's code gets compiled, then at some point every file gets its own module, right?
- kd5bjo 6y ago> If `mod foo;` is the only way to make sure a file's code gets compiled, then at some point every file gets its own module, right? There’s also the `include!` macro which can read a source file without making it a module and the `#[path=...]` directive which can let you use the same source file for several modules.
- pornel 6y agoIf you want to, you can easily make a project comprised of many modules and using only one file (or zero files) Creating a single module from many files is also possible. It can be done with clumsy C-style includes, or more idiomatically composed from `pub use` of items from private modules. `impl` blocks can be anywhere. The point is, files and modules are decoupled. Files just happen to be a convenient default for modules, but they aren't semantically special in Rust. There's no syntax or privacy boundary specific to files. ) Cargo.toml with `[lib] path = "/dev/stdin"` + `echo 'fn main() {}' | cargo build` happens to work :)