Importing my own project module from outside of src/bin/ directory

Hello all.

Can anyone help me ?

When I do as follows:

my_project/
    src/
        bin/
            learn_modules.rs
            garden.rs
//learn_modules.rs

use std::process::ExitCode;

mod garden;

fn main() -> ExitCode {
    
    println!("{}", garden::LOCATION);
    println!("{}", garden::get_m());
    
    return ExitCode::SUCCESS; }
//garden.rs

pub const LOCATION: &str = "Poland";

const M: &str = "garden";
pub fn get_m() -> &'static str { M }

It works :slight_smile:

2).
But when I do:

my_project/
    src/
        bin/
            learn_modules.rs
        modules/
            garden.rs
//learn_modules.rs

use std::process::ExitCode;

mod garden;

bla bla bla ....

it complains

unresolved module, can't find module file: garden.rs, or garden/mod.rs

file not found for module garden
to create the module garden, create file "src/bin/garden.rs" or "src/bin/garden/mod.rs"
if there is a mod garden elsewhere in the crate already, import it with use crate::... instead

I do not know how to handle my scenario.
Please assist.

by default, module hierachy follows the file system hierachy, i.e. a sub module corresponds to a source file in the subdirectory of the parent module. (special case: lib.rs, main.rs and mod.rs logically resides one level higher than their actual file system location, e.g. foo/bar/mod.rs is equivalent to foo/bar.rs).

you can use the #[path="..."] attribute to specify a source file different than the default, though I would not recommend to use this unless in very niche use cases.

so, the recommended solution is to use a lib crate, make the garden.rs a sub module of the lib crate, and then import the lib crate in your bin crate:

// src/lib.rs
pub mod modules {
    // corresponds to `src/modules/garden.rs` or `src/modules/garden/mod.rs`
    pub mod garden;
}

// src/bin/learn_modules.rs
use my_project::modules::garden;
fn main() {
    println!("{}", garden::LOCATION);
}

the alternative solution is to use the #[path] attribute:

// src/bin/learn_modules.rs

#[path = "../modules/garden.rs"]
mod garden;

fn main() -> ExitCode {   
    println!("{}", garden::LOCATION);
}

note, when use #[path] attribute, the module name doesn't need to match the file name, e.g., the following is valid:

#[path="../modules/garden.rs"]
mod lake;
2 Likes

You are right :slight_smile:

Also: best-practices-for-packages-with-a-binary-and-a-library

So I can do:

my_package/
    src/
        bin/
            learn_modules.rs
        lib.rs
        lib/
            garden.rs
// lib.rs

mod lib {
    pub mod garden; }

pub use lib::garden;        /* --- (1) --- */
//learn_modules.rs

use std::process::ExitCode;

// I do not need to
// use my_package::lib::garden;
// because of public reexport garden module inside lib module
// (see /* --- (1) --- */ above).
// Instead just need to
use my_package::garden;

fn main() -> ExitCode {
    
    println!("{}", garden::LOCATION);
    println!("{}", garden::get_m());
    
    return ExitCode::SUCCESS; }

Although the source for a library crate in Rust "starts at" lib.rs, you're not supposed to or expected to use a lib namespace. Just put garden.rs immediately under src/ and change the source of lib.rs to pub mod garden; pub use garden;.

you're not supposed to or expected to use a lib namespace.

Why ? What is rationale for it ?
Can You please elaborate.


Consider the following

src/
    alaka.rs
    kokona.rs
    lib.rs
    moweqi.rs
    zumua.rs

I simply can not understand which are binary crates, and which files constitute library crate, without looking inside them or to Cargo.toml.

But now I do not even need to think, it is obvious:

src/
    bin/
        alaka.rs
        zumua.rs
    lib.rs
    lib/
        kokona.rs
        moweqi.rs

The bin directory for multiple executables is very standard, that is true. But I would argue that with the bin directory, lib becomes unhelpful: you already know all your binaries are in bin, so everything outside of it must be part of your library.

3 Likes

How about:

my_package/
    Cargo.toml
    src/
        bin/
            learn_modules.rs
        lib/
            lib.rs
            garden.rs
# Cargo.toml

[lib]
path = "./src/lib/lib.rs"
// lib.rs

pub mod garden;
//learn_modules.rs

use std::process::ExitCode;
use my_package::garden;

fn main() -> ExitCode {
    
    println!("{}", garden::LOCATION);
    println!("{}", garden::get_m());
    
    return ExitCode::SUCCESS; }

I wouldn't say that's necessarily idiomatic, but it's certainly a lot better. I wouldn't have any problems with it, personally.