How to include a sibling folder

I'm very confused by rust's "use" mechanism. Here is my folder structure:

project/
└── src/
    ├── core/
    |   └── lib.rs
    ├── app1/
    |   └── main.rs
    └── app2/
        └── main.rs

how can I let app1/main.rs and app2/main.rs include core/lib.rs? I tried

use crate::core;
use crate::core::lib;

doesn't work.
this is not the first time I got stuck on this seemingly simple task.
what's the reason behind rust making this not intuitive ?

I'm pretty sure that the line you want is

use project;

or

use project::*;

The problem with intuition is that it's different for everyone, so what's intuitive for one person is confusing to another. In this case, you have to understand some of Rust's build process before the solution makes sense. The common library code and the apps are each a separate crate¹, so using the crate keyword inside app1 will only ever refer to something inside the src/app1 directory— You need to refer to the library like it's something you imported from crates.io.

Additionally, your directory structure differs from the standard one, which may also be causing problems. Usually, your project would be laid out like this:

project/
├── Cargo.lock
├── Cargo.toml
└── src/
    ├── lib.rs
    └── bin/
        ├── app1/
        │   └── main.rs
        └── app2/
            └── main.rs

If you want to use your directory structure instead, you'll need to manually tell Cargo where to find everything.

¹ Probably, but I can't be sure without seeing the contents of Cargo.toml

1 Like

I hope you are not seriously suggesting that Rust developers make the module system intentionally hard to use.

2 Likes

You may not believe it, but Rust's approach is simple. It is just significantly different than what other languages do, which makes it confusing until you learn it. But Rust chose this approach, because it has some advantages:

  • all modules can be discovered from the source code, without scanning directories (unlike Go). This allows controlling of module's existence through normal cfg directives, is fast even if you have lots of other files in the project, and "leftover" files in the filesystem don't get compiled by accident.

  • there's no textual inclusion (unlike C and C++), so modules are properly encapsulated and namespaced.

  • modules are defined by their logical path, not file system path, so the inline mod {} syntax is possible (often useful in macros).

  • mod definition is consistent with definitions of fn, struct, enum, and const, and all of these items can be used in the same way. Other languages treat modules/packages in a special way that is inconsistent with how they define anything else. Your intuition probably expects the special behavior.

5 Likes

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.