DEV Community

Cover image for [Rust Guide] 7.2. Path Pt. 1 - Relative Paths, Absolute Paths, and the Pub Keyword
SomeB1oody
SomeB1oody

Posted on

[Rust Guide] 7.2. Path Pt. 1 - Relative Paths, Absolute Paths, and the Pub Keyword

If you find this helpful, please like, bookmark, and follow. To keep learning along, follow this series.

7.2.1 Introduction to Paths

In Rust, if you want to find something inside a module, you must know and use its path. Rust paths are similar to file-system paths and are somewhat like namespaces in other languages.

There are two kinds of paths:

  • Absolute paths: start from the crate root, using the crate name or the literal value crate (the example below will make this clear)
  • Relative paths: start from the current module, using self (itself), super (the parent), or the current module’s identifier

A path consists of at least one identifier, and identifiers are connected with ::.

7.2.2 Using Paths

Look at an example (lib.rs):

mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }
}

pub fn eat_at_restaurant(){
    crate::front_of_house::hosting::add_to_waitlist();
    front_of_house::hosting::add_to_waitlist();
}
Enter fullscreen mode Exit fullscreen mode

hosting is a submodule of front_of_house, and two functions, add_to_waitlist and seat_at_table, are defined under hosting.

In the same scope as front_of_house, there is also a function called eat_at_restaurant. Inside that function, add_to_waitlist is called once with an absolute path and once with a relative path.

For the absolute path, the function eat_at_restaurant and the front_of_house module containing add_to_waitlist are in the same file, lib.rs, which means they are in the same crate (lib.rs implicitly forms the crate module, as explained in the previous article). So an absolute path starts with crate and proceeds level by level, separating each identifier with :::

crate::front_of_house::hosting::add_to_waitlist();
Enter fullscreen mode Exit fullscreen mode

For the relative path, because the function eat_at_restaurant and the front_of_house module containing add_to_waitlist are at the same level, you can start directly from the module name and still proceed level by level with :::

front_of_house::hosting::add_to_waitlist();
Enter fullscreen mode Exit fullscreen mode

In real projects, whether you use an absolute path or a relative path mainly depends on whether the code that defines the item (for example, add_to_waitlist) and the code that uses the item (for example, eat_at_restaurant) will move together. If they move together, meaning their relative path does not change, then use a relative path. Otherwise, use an absolute path. But most of the time, absolute paths are still used, because then the code that defines an item and the code that uses it can move independently of each other.

Let’s run the code next:

error[E0603]:module `hosting` is private
Enter fullscreen mode Exit fullscreen mode

Both the absolute-path call and the relative-path call report this error. The meaning of the error is that the hosting module is private.

This is a good opportunity to talk about the concept of a privacy boundary.

7.2.3 Privacy Boundary

A module does more than organize code; it can also define privacy boundaries. If you want to make a function or struct private, you can place it inside a module, just like the functions in the previous example—they are inside the hosting module.

By default, Rust makes all items (functions, methods, structs, enums, modules, constants, and so on) private. For private items, external code cannot call them or depend on them. Rust does this because it wants internal details to stay hidden by default, so programmers can clearly know which internal implementations can be changed without breaking external code.

Rust’s privacy boundary also has a rule: parent modules cannot access private items in child modules, which is still meant to hide implementation details; child modules can use all items from ancestor modules, because child modules are defined in the context of their parent and other ancestor modules. To put it another way: a father cannot read his son’s diary, but the son can use his father’s money.

To make something public, add the pub keyword when defining the module.

7.2.4 The pub Keyword

Adding pub before mod makes a module public. Let’s slightly modify the previous code:

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}

        fn seat_at_table() {}
    }
}

pub fn eat_at_restaurant(){
    crate::front_of_house::hosting::add_to_waitlist();
    front_of_house::hosting::add_to_waitlist();
}

Enter fullscreen mode Exit fullscreen mode

Note: both the hosting module and the add_to_waitlist() function need the pub keyword in front of them.

Compile again, and this time the compiler does not report an error. Someone may ask: why does front_of_house not need pub? It is private, but there is no error when calling it.

front_of_house does not need to be public because eat_at_restaurant is defined in its parent module, the crate root, and parent modules can refer to their child modules directly. However, hosting is inside front_of_house, so accessing it from the crate root counts as accessing it from outside front_of_house. Therefore, hosting must be pub. The same applies to add_to_waitlist().

Top comments (0)