11 ms·
The Rust module system is too confusing
- ekidd 10y agoThe Rust module system is a little bit weird. As far as I can tell, this is the result of two things: - 'use' statements are relative to the root of the crate, not the current module. To work around this, you can write 'use self::name' or 'use super::name' for relative imports. Annoying but no big deal. - If you import a symbol into module 'a', it doesn't also get added to 'a::b'. I don't know why I keep expecting this. Once I learned these two things, Rust imports were easy. I'm not sure I agree with withoutboats' proposals. His chnages would make several obvious things work the first time, but at the expense of taking a simple, easy to explain rule and replacing it with something implicit and mysterious. The bigger Rust learning curve issue is making friends with the borrow checker. I like the borrow checker. It has my back, even when I'm trying to write fast code that would be recklessly abusive of pointers in C. But I can't deny that it took me a week or two to make friends with the borrow checker.
- galacticpony 10y ago"I'm not sure I agree with withoitboats' proposals. His chnages would make several obvious things work the first time, but at the expense of taking a simple, easy to explain rule and replacing it with something implicit and mysterious." Which is ironic, considering the premise is that the module system is "too confusing". Having never used Rust, the current rules - as explained by the author himself - seemed straightforward. For the proposed new rules, I'm confronted with two long paragraphs. I didn't bother to read them. If the intent is to make the system less confusing, there should be a way to explain the new rules as tersely as the old ones.
- Manishearth 10y agoA blog post proposal to the Rust community with a rough set of ideas need not fixate on itself being super easy to understand. That's for the RfC to do (we require a "how do we teach this" section on RfCs) That being said, I do feel that the union of a confusing and simple system can often be a more confusing system so I'm wary of this proposal reducing complexity.
- lucaotta 10y ago'use' statements are relative to the root of the crate, not the current module. To work around this, you can write 'use self::name' or 'use super::name' for relative imports. Annoying but no big deal. I'm writing an application. Inside main.rs I can write extern crate foo; use foo::Bar; If I create a module and use the same crate, I need to write: extern crate foo; use self::foo::Bar; This always surprises me and I never found anything explicit written on the Book. To me, it looks like a nice papercut to fix.
- gliptic 10y agoWhat if I really do want the 'foo' crate to be inside the sub-module? If you want it in the crate root, you should put it in the crate root.
- lucaotta 10y agoSorry, I don't really grasp your comment. Maybe I can clarify what I'm trying to do. My typical use case is importing definitions, for example an error type or enum from the crate, let's say something from tokio_core. I'd like to use this enum both in main and in my module; from my (limited) experience, you need to extern crate tokio_core both in main and the module, then use the definition. In this case the syntax is different for somewhat obscure reasons.
- carols10cents 10y agoHm, I'm still confused about what you're trying to do :-/ If you're trying to use something from tokio_core in your own crate, in neither place will `use self` do that, like you had in your first example... it would be `use tokio_core::foo::Bar`... and the `self` was the only difference between the binary and library, so I'm not sure what the paper cut is exactly.
- lucaotta 10y agoBasically I start hacking on a project, then when finally something is working, I want to split it into a separate "something". Coming from the C++ world, I'd create a new cpp/h couple, maybe in its own subdir. I understand that the equivalent in Rust is creating a module. I made a simple example with just two files: (In the meanwhile I realized that my first example was wrong :) https://github.com/lucaotta/rust_modules https://github.com/lucaotta/rust_modules Let's say that after my refactor, I've "moved" the crate import into the module since "logically" it belongs there. In this case the compilation fails if I don't use self::. But previously it was working in main.rs! And I have no idea why...
- the_mitsuhiko 10y agoThe main issue with Rust's module system is that your own modules and crates live in the same namespace. So if you have a `mylib/image.rs` file and you want to use the image crate then you can't without either renaming your `image.rs` or you alias the crate on import. I really wish your own modules would require some relative import and be scoped within `mylib`. Eg you would do `use mylib::image::MyImage` and `use image::OtherImage` instead of `extern crate image as image_crate; use image::MyImage; use image_crate::OtherImage`.
- throwawayish 10y agoSo Rust essentially has the same design issue that Python had before PEP 328?
- Manishearth 10y agoNot exactly; Python had the problem where `import foo` had two different meanings and there was no way of working around it. Rust still understands that crates are different, and will let you do `extern crate foo as bar` to avoid conflicts.
- blktiger 10y agoCorrect me if I'm wrong, but I think the proposed implicit modules in the article would fix this issue. Instead of exposing mylib/image.rs via mod, you'd just import it as an 'implicit' module. So you'd end up with `use image::MyImage` and `use self::MyImage`?
- marcoms 10y agoFor me what's quite unnerving about `extern crate` is that the `extern` keyword has another function, that is to declare foreign functions [0]. `extern crate` on the other hand, is not an extension of `extern` but serves a completely different purpose. 0: https://doc.rust-lang.org/book/ffi.html https://doc.rust-lang.org/book/ffi.html
- deleted 10y ago[deleted]
- neikos 10y agoI fail to see how the usage is different? They both reference things that are 'external' eg. already compiled etc...
- marcoms 10y agoI mean if anything the syntax should me more specialised as you go into sub-keywords, which makes sense in this industry (e.g. ISO 8601). The problem is that `extern` seems to have been taken from C++ and extern crate is almost tacked on (or that's the impression I get). Comparing with other languages, Rust looks to be the only instance where two keywords are required to simply require a module/library, which sticks out to me.
- Manishearth 10y agoNo, you can also define `extern "C" fn foo() {}` with a body, which creates a function that can be called from C.
- tempodox 10y ago> extern crate is like mod, but it declares an external crate dependency instead of a submodule. Naive question: What the hell's the difference? Is a crate not a module? IMO, there shouldn't be a difference. (EDIT for clarity)
- awinter-py 10y agocrate is what gets released / distributed (i.e. a whole 'library', 'package', 'dependency', 'bundle'). Modules are single source files.
- tempodox 10y agoA binary is of course something different from a source file. But that's just a detail. The “user interface” should be the same for both, except maybe in the Makefile / Cargofile.
- awinter-py 10y agocrates aren't binaries -- they're source bundles.
- mathw 10y agoThe way I think of it is that a crate is a bundle of modules. Crates are quite free to make multiple modules public to be used by anything which is importing that crate.
- charlieflowers 10y agoFYI, /s/bare/bear
- awinter-py 10y agoWell, maybe. Or you're viewing the article through your western cultural bias and missed the author's primary intent.
- charlieflowers 10y ago"bare with me" can only make sense if the author is asking us to remove our clothing along with him. I don't think that was his intent. Telling him about the typo was meant as a courtesy to him, because I appreciate his post and want to help strengthen it in this small way.
- awinter-py 10y agoyou're assuming he wants to bare his body. maybe he wants to bare his soul.
- lhnz 10y agoThis was my experience, too. I had to keep checking other crates to see what was expected from me, and managed to finally understand through trial-and-error. The problem with this is that I'm not likely to remember how modules work a few months after finishing a Rust project. I reckon core aspects of a language should be so simple that they don't need any work memorising them.
- nickez 10y agoMine too, or rather, for my first rust project I initially had all the code in main.rs and then I found it very difficult to split up the project into several source files. I think with some better documentation it wouldn't have been that hard.
- mathw 10y agoI think it's the documentation. It took me a few tries to get everything I needed out of the Book to use my own modules, but then it was easy. Although I still don't quite get how macro importing works. It feels like when you #[macro_use] on an extern crate you get all the macros implicitly everywhere without having to issue any use statements. This seems weird and I have yet to try to find an explanation of it. But I've got modules all over my Advent of Code solutions which use macros from nom without having any nom-related use statements.
- steveklabnik 10y agoYeah, I hear you. I've always thought this, and honestly, I think the issue is that I've always found the module system _very_ intuitive, so it's been harder for me to connect with how people struggle to learn it. http://rust-lang.github.io/book/ch07-00-modules.html http://rust-lang.github.io/book/ch07-00-modules.html is the second edition of the book's chapter on it, I think it's much better. > This seems weird Yes, that is how it works. Macros aren't in a namespace. Yes, it's weird. It's an artifact of history; eventually, it will be fixed.
- 10y ago
- awinter-py 10y agoIn keeping w/ rust's philosophy of being 'c++ with marginally better tooling', I'm happy with the current setup. It's light years ahead of the perf overhead of #include + the pain of setting up linker args for libraries. Are the keywords idiosyncratic? Maybe. Can a new user get it working in an hour and learn it in 2 days? Yes. It would be cool if there were a polyglot build system that you could learn once and have the same expectations in every language. But there are downsides to this proposal (not least of which is the fact that when someone names a polyglot build tool, it's often NPM). I doubt the module system is the #1 or even the #5 thing people struggle with when learning rust.
- pjmlp 10y ago> It's light years ahead of the perf overhead of #include + the pain of setting up linker args for libraries. Sadly not yet. My VC++ builds are still faster, because they can enjoy using binary libraries across projects, something that cargo doesn't yet support. Also VS 2015 and the new VS 2017 do have quite a few improvements regarding the linker, incremental builder and C++ modules support.
- awinter-py 10y agodidn't realize #import had landed in msft tools! awesome.
- pjmlp 10y agoSome information is available here. https://blogs.msdn.microsoft.com/vcblog/2015/12/03/c-modules-in-vs-2015-update-1/ https://blogs.msdn.microsoft.com/vcblog/2015/12/03/c-modules... https://blogs.msdn.microsoft.com/vcblog/2016/02/11/compiler-improvements-in-vs-2015-update-2/ https://blogs.msdn.microsoft.com/vcblog/2016/02/11/compiler-... There were also CppCon talks from both Google and Microsoft, but I cannot search for them now.
- pcwalton 10y ago> My VC++ builds are still faster, because they can enjoy using binary libraries across projects, something that cargo doesn't yet support. This isn't anywhere near the top issue that I (or anyone else I know of) encounter with build performance in Rust. Dependent library builds are very parallelizable, so they tend to build quickly. Moreover, the build performance that matters in the day-to-day cycle is mostly rebuild performance, and the binary artifacts are cached in this case. We could spend time adding a global cache of build artifacts, I guess, but all the time we'd spend working on this wouldn't impact my development velocity much at all. Incremental compilation, on the other hand, would.
- jonathanstrange 10y agoIMHO there are many obscure things about Rust for newbies, and its syntax is also less readable than that of Ada. This gives me high confidence that it has a bright future. I'm not being sarcastic here, Rust seems to be a great language (definitely on my list to learn), I'm really just observing. Looking at the success and history of programming languages it seems to me that many if not most professional programmers want some obscurity, a desire that is often disguised as an apparent need for brevity which in reality plays no role with modern editors. Maybe this makes sense, after all it's better to have a language that an incompetent boss or a marketing department cannot just pick up easily to 'roll their own solution'.
- JulianWasTaken 10y agoWhich evidence do you see that points to obscurity or partial obscurity leading to success (or adoption, I can't tell which you're claiming)?
- hubert123 10y agoThe fact that not every language on the planet is an implementation of Actionscript 3
- jonathanstrange 10y agoNot evidence, only subjective gut feelings, and I've had adoption in mind. But I'm hesitant to answer this question, because it will invariably lead to language flame-wars. Anyway, it seems to me that Algol68, Pascal, and Ada are less obscure than C and C++, but the latter are clearly more popular and have proven their worth over a long time. At the same time, implicit-block type procedural languages like Visualbasic and Realbasic/Xojo are popular, but they do not get much love from professional programmers, even though they are maybe the least obscure among all imperative mainstream languages. LISPs are also an interesting case, S-expressions are among the least complicated and easiest to grasp syntax and CommonLisp offers practically everything a programmer could ever wish for (including multiple dispatch) but it never became very popular. But maybe I'm wrong. For example, advanced Lisp macrology is definitely obscure, so that should count in favor of Lisp being more popular according to me 'hypothesis'. Haskell syntax is fairly easy, but monads are certainly obscure, yet Haskell does not seem very popular. Likewise, Python does not seem to be very obscure, it certainly isn't syntax-wise, and it's still very popular. So I don't know, perhaps my assessment was premature. It almost certainly was. ;-) I've just always had this impression that many programmers prefer a certain amount of special trickery and shortcuts over clarity. But perhaps it's the availability of libraries what counts most in the end.
- deleted 10y ago[deleted]
- tveita 10y agoThis matters less once we get good IDE support. E.g. "mod" should be inserted automatically when you create a new submodule, "use" will be inserted as necessary if you try to use something that is not in scope. The other part is to have a set of conventions. Do I put "extern crate" in the root module or in the submodule that uses it? How do I order my use statements? Do I import just the module or each item in it? When the community settles on a standard way, IDEs can follow it.
- mathw 10y agoI'm hoping at some point rustfmt will order my use statements for me...
- sidlls 10y agoUnless an IDE is part of the standard somehow I strongly disagree that "IDEs make it better" is a good argument for preserving a language feature that is confusing. Not everyone uses an IDE, and for certain low level systems programming kinds of things (you know, the things Rust wants to target) sometimes an IDE isn't available or is cumbersome to use. That said I am a bit confused at the confusion. Rust is better than C++ in this regard, in my opinion, even if the system is a tad slower to build still.
- Manishearth 10y agoAgreed. This should be fixed in the docs, diagnostics, and maybe by changing the language in a way withoutboats recommends, but we should not rely on folks having an IDE.
- Ericson2314 10y agoAll you need to do is make `use` paths look absolute with a leading `::`, followed be either name a crate or `create` for the current crate.
- cousin_it 10y agoI think it's possible to design a more intuitive system with less busywork: 1) For imports, the namespace hierarchy should have a one-to-one correspondence with the file and directory hierarchy. 2) For publishing code, instead of modules and Cargo, there would be one huge worldwide repository. You'd be able to freely import anything marked public, and the build system would cooperate with version control to make things work seamlessly. Unfortunately it's probably too late for Rust to adopt such ideas. I wish some new language used them, though.
- steveklabnik 10y agoGiven that crates.io has no namespaces, this is kinda sorta how this already works, I think. Or at least, I don't understand the distinction.
- cousin_it 10y agoWell, you have two tools that can fetch stuff - git and cargo. In my preferred workflow there'd be only one tool. It would also work as a cloud filesystem, like p4fuse.
- steveklabnik 10y agoCargo can use git, but it doesn't primarily. Crates are stored as tarballs on S3.
- cousin_it 10y agoRight, I don't see why that's necessary. The whole world could be one big source control repository where each client keeps only their local changes. So e.g. if you want to make a local tweak to a third party library you're using, you 1) edit their file in your filesystem which is a transparent view into the world repo, 2) recompile your project.
- steveklabnik 10y ago
- zyxzevn 10y agoThey should have looked at the good module system of OBJECT PASCAL (derived from MODULA). It is easy and allows all kinds of protections. The main difference is that in this language a module always is one file, which consists of 2 parts. 1. INTERFACE (public) 2. IMPLEMENTATION (private) The way it is organized makes compilations also very fast. USES only uses the public parts of the module. A LIBRARY is like crate in rust. Sadly, this module system does not use private classes/types in public functions. But that would not be hard to add. I have no idea what Rust tried to accomplish. Being used to Object pascal many other module systems seem primitive.
- pjmlp 10y agoActually that model came originally from USCD Pascal and was adopted by Apple for Object Pascal, but Borland did improved it quite a bit. You forgot to mention, cyclic dependencies were allowed on implementation part. Also later on, Delphi introduced packages, which are a set of units, with just some of them being public. Similar to the upcoming Java 9 package model.
- pcwalton 10y agoHow does this solve any of the problems listed in the article?
- nercury 10y agoRust module system is similar to C#: mod is namespace, extern is assembly reference. The difference here is that there is no separate "project" file, so all references to inner module files need to be described in the parent file.