Imports and Modules
Namespaces
- A namespace is simply a way to distinguish two things that have the same name.
- It provides a scope to the identifiers within it.
Rust supports namespacing in two ways:
- Crates for re-usable software libraries
- Modules for breaking up your crates
Crates
- A crate is the unit of Rust software suitable for shipping.
- Yes, it's a deliberate pun.
- The Rust Standard Library is a crate.
- Binary Crates and Library Crates
There's no build file
- Have you noticed that
Cargo.toml
says nothing about which files to compile? - Cargo starts with
lib.rs
for a library or the relevantmain.rs
for a binary - It then finds all the modules
Modules
- A module is block of source code within a crate
- It qualifies the names of everything in it
- It has a parent module (or it is the crate root)
- It can have child modules
- The crate is therefore a tree
Standard Library
We've been using modules from the Rust Standard Library...
use std::fs; use std::io::prelude::*; fn main() -> std::io::Result<()> { let mut f = fs::File::create("hello.txt")?; f.write(b"hello")?; Ok(()) }
Note:
- The
std::fs
module - The
std::io
module - The
std::io::prelude
module
Prelude modules, like std::io::prelude
, usually contain important traits and you usually want to import all of it with a *
wildcard.
In-line modules
You can declare a module in-line:
mod animals { pub struct Cat { name: String } impl Cat { pub fn new(name: &str) -> Cat { Cat { name: name.to_owned() } } } } fn main() { let c = animals::Cat::new("Mittens"); // let c = animals::Cat { name: "Mittens".to_string() }; }
Modules in a file
You can also put modules in their own file on disk.
This will load from either ./animals/mod.rs
or ./animals.rs
:
mod animals;
fn main() {
let c = animals::Cat::new("Mittens");
// let c = animals::Cat { name: "Mittens".to_string() };
}
Modules can be nested...
~/probe-run $ tree src
src
├── backtrace
│ ├── mod.rs
│ ├── pp.rs
│ ├── symbolicate.rs
│ └── unwind.rs
├── canary.rs
├── cli.rs
├── cortexm.rs
├── dep
│ ├── cratesio.rs
│ ├── mod.rs
│ ├── rust_repo.rs
│ ├── rust_std
│ │ └── toolchain.rs
│ ├── rust_std.rs
│ └── rustc.rs
├── elf.rs
├── main.rs
├── probe.rs
├── registers.rs
├── stacked.rs
└── target_info.rs
Note:
The choice about foo.rs
vs foo/mod.rs
often depends on whether mod foo
itself has any child modules.
The example is from the Knurling tool probe-run
.
What kind of import?
Choosing whether to import the parent module, or each of the types contained within, is something of an art form.
#![allow(unused)] fn main() { use std::fs; use std::collections::VecDeque; use std::io::prelude::*; }
Standard Library
There's also a more compact syntax for imports.
use std::{fs, io::prelude::*}; fn main() -> std::io::Result<()> { let mut f = fs::File::create("hello.txt")?; f.write(b"hello")?; Ok(()) }