Rust from a Gopher – Lessons 7, 8 and 9(levpaul.com) |
Rust from a Gopher – Lessons 7, 8 and 9(levpaul.com) |
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?
// 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.edit: corrected typo
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.
Depending on the case, this may have practical implications or not, but architecturally speaking, it's not good form.
A practical side effect is that having a lot of data structures is going to clutter the outline view in the IDE.
That bites me often times. Does gopls help in this regard? Can any tool besides the compiler help me to answer, what methods in a particular piece of code implement what interface?
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.
Usage in Haskell is a bit more mixed, but I see a lot of 'qualified imports'.
I wish I could just split up complex modules into multiple files without the extra namespacing.
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.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?
For example `std::time::Duration`, `std::path::Path`, std::cmp::Ordering`. I wish everything was just directly in `std` such as `std::Duration`, `std::Path` and `std::Ordering`.
This re-export that the article talks about is doing this change (which I think is great!) while keeping a nice file structure. I think it is a shame that Rust conflates how you organize your code and how the user of your library sees it by default. However this seems like the best compromise.
Example of this pattern in my code: https://gitlab.com/kevincox/mario-solver/-/blob/137ac5dea067... (however since this isn't a library I use `pub(crate)` instead of `pub`. It works just fine with `pub` except you get a warning if the file doesn't have any exports which is a little annoying).
I don't identify as any of them myself, since I'd be some kind of unclassifiable chimera, but the touch of whimsy warms my heart.
I mean it's an adorable crab! Who wouldn't want to join the rustacean legion behind Ferris. (Who is presumably named after Ferrous[Iron] which 'Rust's)
Why does it make you cringe?
If I met a developer who said they were a gopher or rustacean or whatever. I'd feel physically embarrased for them.
But to be honest, computer people were always more prone to cringey stuff than regular folk... (an extreme example would be the classic clip of Stallman eating dead skin from his feet) It's something with our upbringing I guess.
Just as I thought that it couldn't get worse.
The alternative is to say that you're a developer in a given language or a user of the language. You don't have to create a whole tribe around it.
You can still use re-exports (pub use) to hide it from the public API.
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.
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 :)
I'm not implying that it's a bad language though. Like Java, it's a very widely used language in enterprise and industry, and I think that most of the programmers in those languages view programming as just a 9-to-5, white collar job that puts food on the table. Nothing wrong with this view either, but it's not the kind of environment that would create memes or inside jokes.
Rust, Haskell or Ocaml, on the other hand, are exactly the kind of languages that passionate programmers, that spend a lot of time in chatrooms and on forums like HN, would spend their time on.
My enthusiasm for Rust is _because_ it lets me escape from C++. It's probably the same for Python and Go users.
Wait, what? Import statements are recursive in Rust, if shard1::Foo depends on shard2::Foo, you just need to manually import shard1::Foo.
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.
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.
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.
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.
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.
- Other languages like C++ and C# emphasize how much namespaces are _not_ connected to files at all
- I just ignored it and wrote everything in one big file, because the Rust compiler is just as slow either way. Even in C++ it's arguable whether splitting a file will result in faster or slower compiles, because of the outrageous behavior of #include
But once I realized every file is a module, it slowly started to make sense.
> Using this macro is often a bad idea, because if the file is parsed as an expression, it is going to be placed in the surrounding code unhygienically. This could result in variables or functions being different from what the file expected if there are variables or functions that have the same name in the current file.
`include!` is analogous to C’s `#include`: it textually pastes the file’s contentents in place of the macro, but I can’t think of another way that a textual (vs. semantic) include mechanism would work.