There are currently a couple of module system ideas floating around, and most of them have some aspects that I really like, but also some downsides.
I tried to amalgamate the presented ideas and see how they fit into my own vision of what I would find most useful. This is just a draft, and I tried to keep myself from getting too wordy.
I should again say that most of these ideas aren’t my own. The rough structure is very, very similar to @withoutboats proposal. The glob specifications and visibility considerations come from earlier discussions. There is probably nothing in here that I came up with myself.
I’m also not sure if I have left any problematic holes that came up before.
Summary
-
extern::..
paths for items in external crates. -
crate::..
paths for absolute items in the current crate. -
foo
paths are now relative. -
extern crate
drives external crate availability. -
mod
drives crate structure. -
use
brings external items into the local scope. -
pub
specifies global availability. -
mod *
andextern crate *
globs for externally driven structure.
Paths
- Relative:
foo
,foo::other
- External absolute:
extern::cratename
,extern::cratename::other
- Internal absolute:
crate::module
,crate::module::other
- Explicit relative (for macros):
self::child
,self::child::other
- Parental relative:
super::sibling
,super::sibling::other
Local and global availability
- Locally available items can only be accessed from the module itself. Submodules cannot access only locally bound items.
- Globally available items can be used from elsewhere, according to their visibility.
External crate availability
External crates are made resolvable in the current module and all submodules with one of
<vis?> extern crate <ident>;
<vis?> extern crate *;
Where <vis?>
is an optional visibility specifier and <ident>
is
an external crate identifier. The glob specifier *
will make all
externally specified extern crates available.
Without a visibility modifier, the crate can only be accessed locally
from the current module or via an external absolute path
(extern::cratename::item
) from the module or any submodules. The
visibility modifier binds the crate globally as item in the local module
with the specified visibility and makes it globally referable to via
<module-path>::<item>
.
A single glob variant can be combined with any number of non-glob variants, where the glob variant provides the default visibility.
Examples:
// Make all external crates available to this module and submodules
extern crate *;
// Make the `tendril` crate available to this module and submodules
// and make it globally available as `<mod-path>::tendril`
pub extern crate tendril;
// This will make `tendril` available in the outer module, but
// only the `inner` module will be able to access `serde`
extern crate tendril;
mod inner {
extern crate serde;
..
}
Module structure declaration
Submodules are declared with one of
<vis?> mod <ident>;
<vis?> mod <ident> { .. };
<vis?> mod *;
Where <vis?>
is an optional visibility specifier and <ident>
is
a module name identifier. The glob specifier *
will declare all
externally provided submodules, as specified by the filesystem.
Without a visibility modifier, the submodule can only be accessed locally
from the current module. A visibility modifier will make the submodule
available as item of the current module with the specified visibility and
makes it globally referable to via <module-path>::<submodule-ident>
.
A single glob variant can be combined with any number of non-glob variants, where the glob variant provides the default visibility.
Examples:
// Load all submodules, but only make them available to this module
mod *;
// Load all submodules, and make them available to the whole crate
pub(crate) mod *;
// Declare submodule `text` to be available to the whole crate
pub(crate) mod text;
// Declare submodule `api` to be available to everyone
pub mod api;
Importing items into the local module
An item can be imported into the local module with
<vis?> use <import-spec>;
Where <vis?>
is an optional visibility specifier and <import-spec>
is
either a single item path like foo::Bar
or an item group like
foo::{ Bar, Baz }
or a glob like foo::*
.
Without a visibility modifier, the imported items can only be accessed
locally from the current module. A visibility modifier will make the
items available as items of the current module with the specified
visibility and makes them globally referable to via
<module-path>::<item-ident>
.
Examples:
// Make `io` locally available, cannot be referred to globally
use std::io;
// Make a submodule item publicly available as child item of the
// current module
pub use submodule::Item;
Restricting item visibility
An items global visibility can also be specified with
<vis> <relative-item-spec>;
That is: A visibility specifier followed by a relative item path like
foo
or foo::Bar
or a relative item specification like
foo::{ Bar, Baz }
.
The visibility adjustment can also be specified for items in child
modules, but they can only be restricted further than specified by
the submodule itself. In other words: A submodule item that the
submodule considers part of its public interface can be for example
restricted with pub(crate) submodule::Item
.
The specified visibility of a submodule is the default maximum visibility for its items. Further relaxations of the visibility of items of the submodule will also adjust the visibility of the submodule itself, but not of the items that aren’t specifically mentioned.
Examples:
// set visibility of a local item
pub MyType;
// `submodule`s item are by default only available via local access
// from the current module, but `submodule` and ?submodule::Item`
// can be referred to globally.
mod submodule;
pub submodule::Item;
Provided solutions
Used paths are now relative or explicitly absolute
Paths that aren’t fully qualified can only be resolved through locally available items.
The crate root is no different than any other module
An explicit pub
is required at any level to make items globally
available, and paths are relative by default, so referals to the crate
root have to be explicit.
Crates and modules don’t have to be strictly specified
Only requires specifying that submodules actually exist with the glob syntax. Allows for consistent, explicit visibility specification.
External items are identifiable
Due to the extern
keyword at the beginning of the path.
Adjusting submodule item visibility does not require reexporting
Example:
// Autoload all submodules locally, make image available to the
// whole crate, and provide image::Png, image::Jpeg and text::Text
// as part of the current module's API while keeping them in their
// original place.
mod *;
pub(crate) mod image;
pub image::{ Png, Jpeg };
pub text::Text;
Appendix
Requiring some specification of external crates and modules
- Allows specifying default visibility.
- Documents in the module that submodules are autodiscovered.
- Documents that external crates are autodiscovered.
-
extern crate
is useful for restrictions. -
extern crate *
is only one statement and can be inserted bycargo new
.
Possible compatibility with facade pattern solutions
Since this is being discussed elsewhere, I thought I’d note that this should be perfectly compatible:
inline mod *;
pub(crate) inline mod submodule;