Clarifying what "external code" means in Chapter 7.4

I am reading the Rust Book and I got a bit confused by Re-exporting Names with pub use:

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

pub use crate::front_of_house::hosting;

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

Quote:

By using pub use , external code can now call the add_to_waitlist function using hosting::add_to_waitlist.

What is external code in this context? It's not clear from the examples how the code snippet above is different to:

use crate::front_of_house::hosting::add_to_waitlist;

“External code” here means code outside the module where the pub use appears. In the example from the book, that module in the root module of a library crate, so pub makes this path visible in other crates that use this library.

Without this re-export, other crates could not access the hosting module, because it would be available only within the private front_of_house module.

2 Likes

Thanks! Is it an apt comparison to say that pub use in Rust is like a dual import and export in JavaScript ES6?

TL;DR: yes, and no.

Longer explanation:

At a very high level import is comparable to use (no pub) but export has no single direct Rust analog. The reason is that Rust has pub use which is all at the same time an import and a re-export of that imported item. But there are also privacy controls on modules, functions, types etc., e.g. the pub in pub fn foo() {} which also have a strong effect on what's being exported from a module.
Javascript's export more or less can do both of those things so in that sense it's more flexible.

Which brings me to the fact that rust is oriented around the module concept which by definition consists of one or more files AFAIK, vs the resource-oriented nature of Javascript imports and exports (which does have finer controls on what is imported, of course).

1 Like

HEy @jjpe, thanks for your explanation. There's one sentence where I lost you:

What are the two things that you're referring to?

These discussions about Rust modules and use always confuse the socks of me.

For example: As far as I can tell use does not import anything in Rust. All it does is provide a shorter way of referring to something that is already available. I can use `use' like this:

use pcg::Pcg;
    ...
    let mut pcg = Pcg::default();

But I don't need use to import what I want, I could just use it as is:

let mut pcg = pcg::Pcg::default();

I conclude the actual importing has been done by Cargo.toml.

This is very different to what goes on with 'import' in JS or 'require' in node.js.

Similarly it looks to me as if pub use in Rust is not exporting anything either. It's only making a different way of referencing something that is already available.

Or am I missing something here?

1 Like

Not quite. Consider the following:

mod public {
    
    mod private {
        pub const ZERO: u8 = 0;
    }
    
    pub use private::ZERO;
    
}

fn main() {
    // works; but would not work without `pub use` in `mod public`
    println!("{}", public::ZERO);
    
    // error[E0603]: module `private` is private
    // println!("{}", public::private::ZERO);
}

Playground

Hmm.... interesting.

Once could make everything public, then no pub use is required:

pub mod public {    
    pub mod private {
        pub const ZERO: u8 = 0;
    }
}

fn main() {
    println!("{}", public::private::ZERO);
}

I'm just not sure I would call any of this "importing" or "exporting" in the JS sense. It's all very different. As I said, always confuses the socks off me.

@PaulRBerg
The first is exporting symbols.
The second is privacy control: deciding what's accessible from outside the JS module/resource, and what is not, and it takes the shape of deciding what to export.

@ZiCog

Well what is an import? Regardless of how it's actually implemented (and indeed there are a fair amount of differences between JS and Rust in that matter), conceptually it can be seen as making a symbol defined externally to the current module directly accessible from that same current module. In that sense, it's an import. A similar reasoning holds for re-exporting with pub use.

1 Like

Technically crates are not linked when they are not used in the code, even when they are defined as dependency in Cargo.toml. Most of the time this doesn't matter, but sometimes a crate for example exports an unmangled symbol. In that case you may need to use use my_crate; without referring to my_crate later on just to link my_crate. As another example bevy_dylib contains a use bevy_internal; line to ensure that all bevy crates are linked into libbevy_dylib.so. bevy then contains a #[cfg(feature = "dynamic")] use bevy_dylib; to dynamically link to libbevy_dylib.so when the "dynamic" feature is enabled. This means that all crates of bevy will be dynamically linked, thereby significantly reducing the time necessary to link the final executable.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.