Learning Rust – Day 4 – Understanding Modules
geekabyte.io
geekabyte.io
It pretty much instantly became obvious how to use them, and in retrospect, I don't know why it was so confusing. I think it's a good example of how important it is to [a] apply deliberate practice/learning and [b] have empathy for others that haven't (yet) progressed to our own level of understanding.
Not sure whether this is truly better, but it does make it much easier to search for the file containing the module foo -- with the old way, you would have one mod.rs per module which quickly got painful when searching.
But there's also a relatively early moment when you get to a local maxima and suddenly it all starts coming together.
Why is Rust so different on this seemingly boring topic?
And in what regards C++, we have proper modules now, which also don't map into a specific file.
Java now has a mix of packages and modules, because not all packages should be public to start with.
Ada introduced a similar concept in 1983.
C++ modules builds on similar ideas, with module partitions.
Then we have plenty of other less mainstream languages.
rust:
top-level for a crate: lib.rs
module: $modulename.rs or $modulename/mod.rs or a `mod $modulename` block in lib.rs
sub_module: $parent/$sub_module.rs or $parent/$sub_module/mod.rs or a `mod $sub_module` block in $parent/mod.rs
python:
top-level: have the right directory or file in python path
module: $module_name.py or $module_name/__init__.py
sub_module: $parent/$submodule.py or $parent/$submodule/__init__.py
ruby:
top level: have the right file or directory in import path
module: $libname.rb with `module $libname` block
submodule $libname/$submodule.rb with `module $submodule` block
go:
top-level: somewhere in gopath have dir $module that declares `package $module` (convention, you could have a different package name)
module: all files in $module_name that declare `package $module_name`
module: $moddir/$submodule where module files declare `package $submodule`
It's not wildy different. Whats this FUD about?
For a programming language to get modules right (so that they actually give you modularity in big projects) is very difficult.
I had a website with a two-character domain name that talked about good module systems, but I can't remember it. Anyway, there they said basically that with libraries, what you want is your library to be able to split up into several abstract modules, whose names and exports are part of the API (!). Inside each module, you have functions, data structures etc. So you'd have library libtcp having modules "auth", "transport" and "data_link" or whatever. Those in turn depend on symbols from other modules in this library or another library. The point is there's always the extra module layer in-between. A lot of languages don't have this extra layer, and not having it is a grave mistake for big projects.
C has no modules or namespaces. That's obviously bad.
C++ has namespaces (kinda what Java calls "packages"), but no modules in the meaning above, and no formal libraries. That makes the whole thing a mess with global state of other libraries intermingling with your library etc. I've had a long-time embedded C++ programmer friend stop programming C++ entirely because he just couldn't take it anymore how unrelated crap you don't care about fucks up your own library just because you #included something and that #included something you don't care about. Obviously, don't design a programming language like that. (they are trying to introduce proper modules into C++ now, but imo it's too late)
So the summary why Rust does it how it does: Because to do it this way it makes good programs.
Rust's "mod" statement defines a module. Naturally, that means that by default you have nothing from before in that module. It does NOT automatically import (or for that matter, export) things (that would destroy modularity), but if you want to you can do "use super::foo" to get some function from the parent module if you absolutely have to, or "use libx::auth" to get some module from some other external library, or "use crate::auth" to get some other module from your own crate (crate means library or program).
If you can have one module nesting, it's nice to also have multiple module nestings, a module defined inside a module.
You need to have a root module. And that's either lib.rs or main.rs, depending on whether you mean the library or the main program.
Other than that, the "mod" statement is pretty similar to how Python does it: if you say "mod x", it will try to find x.rs, and if that's not there x/mod.rs.
Python would be: "import x", it will try to find x.py, and if that's not there x/__init__.py.
The Rust extension is that you can also write mod x { ... contents here } and that's a neat gimmick.
To say that a module system is mundane (like the article says) ignores that a lot of progress in system design scalability in the past >30 years came from improvements in modularity. It is anything but mundane in big programs. There are entire programming languages that are named after their module capability (for example Modula)
It is clang that it is dragging behind ISO C++20 support.
Apparently all the leachers see no value improving upstream.
This isn't a knock on the C# language, just that I can understand the explicit nature and how it makes the tooling simpler and more consistent.
Also a recent example that is even more explicit is Deno's module system where you tend to explicitly specify a given file/url. It irks me to no end that using this behavior with TypeScript in Node is so broken by comparison (can't reference files with extension).
The older mod.rs scheme seems like an unneeded special case to me, but even that isn't exactly alien compared to what other popular programming languages have done before.
Actually, no, Python uses files as modules.
I guess if you want to be pedantic in regards to Python, yes you're right.
So when you try to move a module into its own file, you need to keep the `mod foo` in the original file.
with foo; -- Import
use foo; -- open namespace
Seems familiar.Can someone help me how to import a module in lib/m.rs from lib.rs ? I've never seen someone asked about it.
First, the `mod` keyword takes a module that the current file/scope can see and makes it available within that file/scope as if it had been defined there. This is necessary to make the module's items accessible, i.e., specifiable by their path within the module, in the current file/scope.
Then the `use` keyword takes an already-accessible member of a named scope -- a module, a type, etc -- and makes it usable by name (i.e., without scoping on each use) in the current scope.
src/lib.rs
lib/
mod.rs
m.rs
In lib.rs use lib::m;
In lib/mod.rs pub mod m;
You need the mod.rs file in subdirectories.