We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Modules, Visibility, Testing and Docs step 2 of 22
Paths: absolute, relative, crate::, self::, super::
A module path tells Rust where to find a name. crate starts at the root,
self starts at the current module, and super moves to its parent.
Practice those movements by implementing a small path-normalization function:
pub fn canonicalize(current_module: &str, path: &str) -> Result<String, String>
This exercise rewrites strings using the rules below. It does not inspect a module tree, resolve imports or check whether an item exists or is public. The result is a normalized path, not proof that rustc would accept it.
Inputs and normalization
current_module is already normalized: "crate" for the root, or a path such
as "crate::a::b". It has no whitespace, empty segments or qualifiers after
the initial crate.
Split path on :: and trim whitespace around each segment. An empty path,
a whitespace-only path, or any empty segment returns
Err("empty path".to_string()). Check this before processing qualifiers.
For example, "x::", "x::::y" and "super:: " all have this error.
After trimming, process the path from left to right:
- An initial
crateselects the root. An initialselfkeeps the current module. Consume at most one of these initial qualifiers. - Each following
supermoves up one level. ReturnErr("above crate root".to_string())if a move would go above the root. - If no qualifier was consumed and the first segment is
std,coreoralloc, treat it as an external crate name. Join all segments with::and return that string without acrate::prefix. - Otherwise, append the remaining segments to the selected module and return
its full path beginning with
crate.
Apart from the empty-segment cases, inputs use plain identifier segments and
qualifiers only in the positions described above. You do not need to validate
identifier syntax or handle generics, aliases, macros or later qualifiers such
as a::super::b.
A path may end after its qualifiers. From crate::a::b, self returns
crate::a::b, super::super returns crate, and crate also returns crate.
Examples
These examples all start in crate::a::b:
| Input path | Result |
|---|---|
crate::x::Y |
Ok("crate::x::Y") |
self::helper |
Ok("crate::a::b::helper") |
helper |
Ok("crate::a::b::helper") |
super::sibling |
Ok("crate::a::sibling") |
self::super::x |
Ok("crate::a::x") |
super::super::super::x |
Err("above crate root") |
std :: collections :: HashMap |
Ok("std::collections::HashMap") |
self::std::thing |
Ok("crate::a::b::std::thing") |
crate::std::thing |
Ok("crate::std::thing") |
The table omits .to_string() around result strings for readability.
The function returns owned String values for both success and failure.
How this relates to real Rust
Real paths also depend on names in scope. A local module or an import can change what a bare name refers to. The exercise deliberately gives the three external names priority and treats all other bare names as local.
Here is a complete example showing the difference. The nested module named
std shadows the external crate when accessed through a bare std path.
In edition 2018 or later, ::std explicitly selects the external crate.
mod outer {
pub const VALUE: u32 = 7;
pub mod inner {
mod std {
pub const VALUE: u32 = 9;
}
pub fn check() {
assert_eq!(crate::outer::VALUE, 7);
assert_eq!(super::VALUE, 7);
assert_eq!(self::super::VALUE, 7);
assert_eq!(self::std::VALUE, 9);
assert_eq!(std::VALUE, 9);
assert_eq!(::std::mem::size_of::<u32>(), 4);
}
}
}
fn main() {
outer::inner::check();
}
A leading :: is valid Rust syntax, but this exercise's string format treats
it as an empty first segment. Do not add a separate global-path rule.
The fixed external-name list is also an exercise rule. In a normal std build,
std and core are available through the extern prelude. alloc is not
automatically included; it can be brought into scope with extern crate alloc;. Other external names depend on the build and crate declarations.
Having no Cargo manifest does not itself determine the extern prelude.
When a real path fails to compile, use the diagnostic to locate the problem. E0432 reports an unresolved import, E0433 a resolution failure, and E0603 an inaccessible private item. The surrounding message matters: a missing crate, a misspelling and an incorrectly qualified path can require different fixes. For a private item, look for its intended public API before changing visibility.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.