I have read all the documentation on multi-module setups. My application is not a library but a bin application. I consistently have a problem with things not resolving whenever I add new code and/or new rust files. Everything is defined in main.rs which should be the root. I know that this subject comes up often and I feel it is detriment to a language that I like. Can someone PLEASE direct me to some coherent documentation on the proper way to do things. I have read the book until I am bleary eyed and as far as I can tell I am doing the right thing. Also I use IntelliJ which has its own problems with Rust. I frequently have to reboot and resolution problems disappear. I'm considering cancelling IntelliJ for that reason. I opened VScode and it complains some things are not resolved while IntelliJ has shows no error.
When you say "things not resolving", do you mean cargo build
gives you a compilation error, or are the problems solely in IntelliJ? (I'm trying to determine if this is an IDE question or a Rust language question.)
Could you give more detail about how your modules are currently set up?
cargo build does not build cleanly. Typically there are items that do not resolve. Sometimes items appear in Intelli appear not to resolve but cargo build runs without complaint. As mentioned there are times I have to quit Intelli and reopen the project. Then items that did not resolve earlier get found. I'm going to open a trouble ticket with IntelliJ as I pay for the IDE and these recurring problems are annoying.
I'm sorry I cannot share the code on GitHub but it is proprietary.
I have about seven separate folders off src filled with rust sources. Each has a mod.rs which lists each source. Each mod.rs has a pub(crate) mod for each source file. There are no inner folders. The main.rs contains a pub(crate) entry for each of the separate src folders.
I had created a new subfolder off src for more sources with new functionality. It was setup like everything else but no matter what I tried nothing in that folder could be found by other sources in the project. Intellij insisted on putting 'tokio' are the start of the path when I used the Intellij import tab. Finally I scrubbed the branch and started over.
I recreated the new folder and created an empty mod.rs. Then I moved the new source files into that folder individually and registered in mod.rs. Then I added the mod for the folder in main.rs. I went into Intellij and imported each unresolved function.
This was very time consuming but finally everything got resolved and the cargo build succeeded.
It seems whenever I add to the project I have these types of problems. The need to close and reopen the projects leads me to believe it is an Intellij issue. I pay for an Intellij license and do not appreciate these issues. I've never had this nonsense with other languages.
If there is anything I have related that is causing my grief I would appreciate to know what I am doing wrong. Thanks.
Binary projects also have a secondary root lib.rs
by default, which maintains a completely separate module hierarchy. Maybe you’ve accidentally put some of your modules there instead?
I do not have a lib.rs in my project.
a relatively small portion of users on this forum use Intellij
, most probably are using rust-analyzer
together with their favorite lsp-compatible code editors.
I think Intellij
uses rust-analyzer under the hood, but it's a proprietary product, the tech support team probably could help you better and quicker.
if you want to be sure it's an IntelliJ
issue rather than problems of the code itself, maybe try a different editor, such as the battle-tested vscode/vscodium, or the new fangle zed, which I believe provide the most seamless zero-config, out-of-the-box experience of rust-analyzer integration.
for vscode, you just download it, open a rust project, and a notification should pop up, prompt you to click a button to install the recommended rust extensions. if you don't see the pop up, just search rust-analyzer
in the extensions side panel.
IntelliJ/RustRover don't use rust-analyzer.
thanks for the clarification. I didn't know much about those.
I also have vscode and use it to cross check issues I see with IntelliJ, I have also created a ticket with Intellij for the problems I see.
This is what makes me nuts. This is a screenshot from Intellij. The items in red cannot be resolved. But the use statement must be correct because the project builds and runs. On VSCode it also builds but tells me to add the same use statementceven though it is there. But the project still builds and runs If I tell the either IDE to resolve the items then I get new generate use statements that cause the build to fail. And even if the use statement is there then VSCode tells me to add it.
The pb is a module defined in server.rs which refers to protobuf generated code. If I tell Intellij to resolve the items is generates a use server::pb::{CreateUserRequest} which causes the build to fail.
I'm sure I'm doing something wrong but neither IDE tells me what is really wrong.
Should I create a lib.rs and move all the mod definitions from main.rs to there? And if that doesn't help what else can I do ?
this is very strange, I never saw similar errors.
is your project on some remote file system? does the error disappear when you save the file and trigger the "check-on-save"?
Even if I add the suggested statement VSCode insists I add it. So I add it and VSCode tells me to add it again.
Everything is located on a 2TB MacBook Pro. I have never had filesystem problems. I always save the code before I build it. I know VSCode doesn't do this automatically but Intellij does before building.
Not even the Cargo.toml
file(s) and the file tree?
I think I might have found what is wrong. I renamed main.rs to lib.rs and got the items to resolve by putting server at the start of the use path.
From what I have read a binary project uses main.rs as the root but my quick experiment seems to indicate that lib.rs is always the root (or IDEs always assume lib.rs is the root).
Can anyone confirm this?
No, that is not correct. Cargo looks for various files and if it finds them, each one becomes its own, separate crate root file belonging to its own “Cargo target”, including src/lib.rs
and src/main.rs
.
Presuming your package is named “server
” (in its Cargo.toml
), this sounds like you have a src/main.rs
and another crate root, like maybe tests/some_test.rs
(a test target). From another crate you can only use items from that crate and your package’s library crate — it is impossible for tests/some_test.rs
to refer to items in src/main.rs
, since tests/some_test.rs
is a different crate root and src/main.rs
does not make a library crate.
If your goal is to make one binary, then tests of code in it should go in child modules of src/main.rs
, and integration tests of the whole binary (that actually run it) should go in the tests/
directory under the project root.
On the other hand, if you have more complex needs, it might in fact make sense to keep having a library (src/lib.rs
) so you can have multiple test targets. I recommend against this if it is not needed, since it can increase confusion.
My goal is to have a server module and client module. The bulk of the system is in the server. The client module shares the protobuf code generated as a module in the server but does not call anything directly in the server except through gRPC. Tests are not in a separate test module but accompany the code which is tested.
It sounds like this arrangement should use lib.rs as the root. The client side of gRPC generated code would qualify as library code but the server code need to be separate. That is why I kept client and server separate.
you are not limited to a single root, each crate (library, binary, test, example, etc) is its own root. read the linked documentation by @kpreid for details.
if you have multiple binaries which share common code, you should put the common code in a library crate, and import the library in the binary crates.
note, the crate
keyword refers to the root of the current crate, so it has different meaning in different crates, I think that's where the confusion comes from. for example:
# Cargo.toml
# package.name is mandatory, but is usually generated using `cargo new`
[package]
name = "foo"
# the following sections are example of the cargo auto discovered crates
# you can omit them if you don't plan to override the default
# the `lib.rs` corresponds to a library crate with same name as the package
[lib]
path = "src/lib.rs"
name = "foo"
# each `NAME.rs` (or `NAME/main.rs`) file in the `src/bin` directory
# corresponds to a binary crate
[[bin]]
path = "src/bin/server.rs"
name = "server"
[[bin]]
path = "src/bin/client/main.rs"
name = "client"
// lib.rs
// the generated probobuf module
// corresponds to the file `src/pb.rs` or `src/pb/mod.rs`
pub mod pb;
// in the library crate, `crate` refers to the root of `lib.rs`
pub use pb::StatusPacket;
// src/bin/server.rs
// the correct way is to imort it from the library crate
// `foo` is name of the `lib.rs` crate, importable as an extern crate
use foo::pb::CreateUserRequest;
// within in this crate, keyword `crate` refers to the root of
// `src/bin/server.rs`, not `lib.rs`
use crate::pb::XXX; //<-- error: cannot find `pb` in `crate`