Search code examples
modulerustcode-organizationrust-crates

Cross-module function call in Rust


I'm trying to call a function belonging to some module from another module (for code factoring, organization, etc).

Here is my crate structure:

➜  mod_test git:(master) ✗ tree
.
├── Cargo.lock
├── Cargo.toml
└── src
    ├── bin
    │   └── one.rs
    ├── lib
    │   └── two.rs
    └── main.rs

3 directories, 5 files

In main I declare:

pub mod bin {
    pub mod one;
}
pub mod lib {
    pub mod two;
}

and all these files simply contain a trivial pub fn main() {println!("hello");}.

At this point, everything is okay.

Now, is it possible to call lib::two::main from bin/one.rs?

None of use crate::lib::two;, use super::lib::two;, use self::super::lib::two; added in bin/one.rs work.


  • edit: I have: rustc 1.42.0 (b8cedc004 2020-03-09) installed on Linux 5.3.0-45-generic, for what it's worth.

  • edit 2: whenever using the super keyword, I get this mysterious error from rustc:

error[E0433]: failed to resolve: there are too many leading `super` keywords

and I can't find any troubleshooting about this anywhere.

  • edit 3: adding a lib.rs file in src declaring the lib module structure, and writing use mod_test::lib::two; in one.rs works, but:

    1) it defeats the idea of not multiplying "dumb module declaration files" in my crate.

    2) I have to literally copy the exact same information at two different places (in main.rs and in lib.rs)

    3) use mod_test::lib::two; is the only working syntax, using crate or super keywords still result in arcane compiler errors


Solution

  • src/bin is a special directory name to Cargo. Files in this directory are compiled as standalone binaries when you run cargo build. When compiled as binaries, they aren't part of the crate structure defined in main.rs or lib.rs.

    If you just want to use bin::one as a module inside main.rs, what you have works already! You're getting error messages from compiling one.rs as a standalone binary, not from compiling main.rs with bin::one as a module. If you run cargo run --bin <name-of-project>, compilation will succeed and it will run the program in main.rs.

    To tell Cargo not to compile one.rs by itself, I would suggest renaming the bin directory. This not only solves the technical problem, but also is less likely to confuse other programmers reading the project, who will expect bin to contain binaries. There may be some way to prevent Cargo from treating bin specially in this way; however, renaming it is probably the best option.

    If you do want one.rs to be compiled to a separate executable that uses two, you must create a lib.rs file in the same directory as main.rs. This is also a special file to Cargo and it defines the module structure for the library crate.

    // lib.rs
    pub mod lib { /* note: this lib is not related to lib.rs; just unfortunately named */
        pub mod two;
    }
    

    Then inside one.rs, write use <crate-name>::lib::two;

    // bin/one.rs
    use mod_test::lib::two;
    

    crate::lib::two does not work, as files inside the bin directory are compiled as standalone binaries, not as crate members; therefore, you have to call the crate by its "external" name.

    adding a lib.rs file in src declaring the lib module structure, and writing use mod_test::lib::two; in one.rs works, but:

    1) it defeats the idea of not multiplying "dumb module declaration files" in my crate.

    2) I have to literally copy the exact same information at two different places (in main.rs and in lib.rs)

    main.rs and lib.rs are two different crate roots. They are allowed to have different structure. You don't need both unless you want to generate both the binary and the library. If you want to use the library crate from inside any binary (including main.rs), it's just a use away:

    use mod_test;
    

    See also