Skip to content

← Modules, Visibility, Testing and Docs step 2 of 22

Easy Primitives

Paths: absolute, relative, crate::, self::, super::

Once there is a tree, you need to name things in it. Rust gives you four ways to start a path, and getting them confused is the single most common early module failure.

pub fn canonicalize(current_module: &str, path: &str) -> Result<String, String>

Given the module a path is written in — always in canonical form, "crate", "crate::a", "crate::a::b" — resolve the path to its canonical absolute form.

The four beginnings

written means
crate::x::Y start again at the crate root
self::helper the current module. Explicit, and sometimes required for clarity
super::sibling the parent module — one hop up the tree
helper relative to the current module, like self::helper

They compose. super::super::top climbs twice. self::super::x is legal (and redundant — self sets the anchor to where you already were, then super climbs from there). What is not legal is climbing past the crate root: super from crate is a hard error, not a silent no-op. Return Err("above crate root".to_string()) for that, and Err("empty path".to_string()) for a path that is empty or blank.

The fifth beginning, which is not in the tree at all

Since edition 2018 a bare leading identifier is resolved against the current module and the extern prelude — the set of external crates linked into your build. So std::collections::HashMap written inside crate::a::b does not mean crate::a::b::std::…; it means the std crate.

Under this harness the extern prelude contains exactly std, core and alloc, because there is no Cargo.toml and therefore no dependencies. Treat an unqualified leading std / core / alloc as an external crate and return the path unchanged. Note the word unqualified: self::std::thing names a module of your own called std, and crate::std::thing likewise. That distinction is exactly why self:: exists.

Three errors that beginners lump together

You will meet all three in real code, and they mean genuinely different things:

  • E0433 — failed to resolve. The name is not in the tree. You misspelled it, or you never declared the mod.
  • E0432 — unresolved import. A use declaration names something that does not exist at that path.
  • E0603 — is private. It exists, at exactly the path you wrote, and you are not allowed to see it. This is good news: the path is right and only the visibility is wrong.

Reading the code rather than the prose saves a lot of guessing. “Not found” and “found but forbidden” call for opposite fixes.

Contract

The qualifier run (crate / self / any number of super) only ever appears at the front of a path; rustc rejects a::super::b long before a function like this would see it, so you do not have to handle it. Segments are plain identifiers separated by ::.