rossabaker.com
Rust Book, Chapter 7: Packages, Crates, and Modules
The way Rust organizes code can be tricky for Scala developers. The concepts do not map one-to-one. Further confusing matters, Rust packages are not the closest thing to Scala packages. There are loose translations to Scala to help us get our bearings.
## Packages and crates #
In Rust, a crate is the unit of compilation. It’s either a binary crate or a library crate. The binary artifact is either one executable or one library. It’s a bit like a Scala `.class`, which is runnable if it’s an object with the right `main` signature, or may be added to the classpath to be loaded and linked with other code. A crate is coarser: multiple Rust files may compile into one crate, while it’s common for a single Scala file to compile into multiple clases.
Rust crates are bundled into a package. It may have multiple binary crates and optionally a library crate. This is close to a Scala `.jar`, which bundles library classes, any number of which may be runnable.
## Modules #
A Rust module is closer to Scala packages than Rust packages are. It’s a means to organize code into logical namespaces.
Scala pcakages are typically organized into files according to Java conventions, but a single package can be split across arbitrary files on the source path. Rust modules are more prescriptive: the module name is reflected in the filename, and the hierarchy in the directory structure.
### Visibility #
In Rust, visibility is private by default. In Scala, it’s public.
If a definition is public in Scala, it can be imported and accessed anywhere. The Rust equivalent is `pub` on the definition within the module plus `pub mod` where the module is included.
If a module is included with `mod` instead of `pub mod`, it’s similar to declaring it package private in Scala.
Because Rust does not have subtyping, we are spared the complications of Scala’s `protected`.
## `use` #
Rust’s `use` is about like Scala’s `import`, where `::` is the delimiter instead of `.`.
Importing the full path of structs and enums in Rust is idiomatic, but that functions are typically referenced via their parent. This is different from Scala, where functions are often imported into the current scope either to be found implicitly or to be referenced tersely.
Rust has `pub use` to re-export definitions as though they originated in the using package. Scala 3 introduces `export` for similar functionality, but it requires manual forwarders in Scala 2.
The wildcard import in Rust is `*`. Scala 2 uses `_`, but many projects use `*` via a compiler flag for Scala 3 compatibility.
## `foo.rs` vs. `foo/mod.rs` #
Rust allows module `foo` to be declared in either `foo.rs` or `foo/mod.rs`. Scala package objects may similarly live in either `foo.scala` or `foo/package.scala`, but also `something/nonsensical.scala`.
The book prefers `foo.rs`, but not all Rust developers agree. If `foo` has its own submodules, it will be a directory anyway, and `foo/mod.rs` is nice. Some developers may find many files named `mod.rs` awkward, and it’s more common than `package.scala`.
My opinion in both Rust and Scala is agree to one convention on the project, be consistent, and pick your battles elsewhere.