1

How do you get rust to recognize a module within a crate? I believed it was enough to declare mod [module name] in lib.rs. Is it not?

Error:

error[E0432]: unresolved import `crate::a`
 --> src/bin/b.rs:2:12
  |
2 | use crate::a::f;
  |            ^ could not find `a` in the crate root

src/a.rs:

pub fn f() {}

src/bin/b.rs:

use crate::a::f;

fn main() {}

src/lib.rs:

mod a;

Cargo.toml:

[package]
name = "m"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[[bin]]
name = "b"
path = "src/bin/b.rs"

[dependencies]
Herohtar
  • 5,347
  • 4
  • 31
  • 41
Test
  • 962
  • 9
  • 26

2 Answers2

4

You are confusing crates and packages. As per the corresponding chapter in the book:

A crate is the smallest amount of code that the Rust compiler considers at a time. [...] A crate can come in one of two forms: a binary crate or a library crate.

This means that your lib.rs and bin/b.rs files define two separate crates and thus crate refers to different things. Both belong to the same package which is defined by your Cargo.toml file. To use functions from your library crate in your binary crate, use the crate name instead of crate. In your case, the crate name is the same as the package name, which is m. Note that you will have to mark your library items as pub to use them in another crate.

An example of this can be found in the bat program, which uses both crate and bat in the imports of the bat binary crate:

Niklas Mohrin
  • 1,756
  • 7
  • 23
  • This seems to suggest that in a package with multiple binaries, there ARE multiple crates? This seems different from what locke was saying? – Test Sep 24 '22 at 22:43
  • Yes, each binary is a _binary crate_ and every package can have at most library crate and any number of binary crates. – Niklas Mohrin Sep 24 '22 at 22:46
  • @Test I think that the other answer is particularly unclear in some regards and nudging you in the wrong direction. Your file structure is correct, you only need to understand the implications of it. Separating into more packages will _not_ be the easier solution to your problem. Of course, judge for yourself from the linked documentation and examples – Niklas Mohrin Sep 24 '22 at 22:57
0

When a binary is compiled it is seen as the root of the project structure. So lib.rs is not compiled for binary targets and you can not use any of its modules unless you also add them in your binary. The most common approach is to put your binaries in the src folder with lib.rs so you don't need to worry about the folder structure when using your modules. I suppose you could use include!("lib.rs"); at the top of your binary to make it inherit everything from your library, but I have not tried it and it would likely be considered bad form.

However, you can sidestep the issue by making your binary depend on your library. The easiest way to do this is by making your binary its own crate which depends on your library.

There are also some ways to get around this, is by making a crate a dependency of itself, but these solutions always make me feel a little uneasy about the stability of the solution. This answer can give you a general idea of what that looks like.

Here are some examples various solutions. In case you are wondering, these are just the first crates I found that fit the design pattern. I do not know what some of them do/contain.

Locke
  • 7,626
  • 2
  • 21
  • 41
  • Ok, makes sense. Weird. So any binary is the crate "root". But is it itself a crate? I thought there was only 1 crate here—not the outer lib.rs crate and another crate for the binary. – Test Sep 24 '22 at 21:58
  • Yea, that would be one way to think of it. You could also make your binary an example since examples will import your crate and its dependencies automatically, but it might also make your intentions less clear. – Locke Sep 24 '22 at 22:05
  • What is an "example"? Is that a rust module system term? – Test Sep 24 '22 at 22:12
  • It involves creating an `examples` folder in the root of your project. You then point to examples by name or path similarly to binaries. It is intended to enable you to write examples for users of your crate on how it could be used in practice, but it generally functions the same as a regular binary in most aspects. You probably want to use the subcrate or workspace member solution though. – Locke Sep 24 '22 at 22:19
  • Ok, so to be very clear, writing `crate::` does NOT refer to the "crate". There is only 1 crate. It referes to the "root" of the thing being compiled. There is no "the crate root". There is 1 crate and multiple roots. This seems very confusing. `crate` doesn't actually refer to the crate, it refers to a particular root, which may depend on what binary is being produced? – Test Sep 24 '22 at 22:42
  • Yea, that is the general gist of it. The main issue is you don't want the code related to binaries being compiled for the library and vice-versa. In a way, every root thinks they are *the crate* and it just makes a couple output forms of that crate. Its much easier to avoid confusion if you separate your library and binary into separate workspace members. – Locke Sep 24 '22 at 22:49
  • @Locke What do you mean by "making a crate a dependency of itself"? This is not possible. By reading your comments, it appears that you too are confusing _packages_ and _crates_. In particular, the term "root" is not a well defined part of Cargo terminology and no root "thinks they are the crate". What you call roots are actually several crates already – Niklas Mohrin Sep 24 '22 at 22:51
  • Furthermore, `include!("lib.rs")` should generally not be used unless one has a really good reason for it – Niklas Mohrin Sep 24 '22 at 22:55
  • @NiklasMohrin Its a weird concept, but I have seen in done in a couple strange cases. It often mostly involves adding the crate as a `dev-dependency` of itself for tests or examples. It seems to work by building to crate once under the rust lib target, then using that as a dependency for the thing you are actually compiling. I'm not a fan of this design pattern due to the weirdness going on and the existence of better solutions in most cases. I was more just tossing out ideas. – Locke Sep 24 '22 at 22:57
  • As for crates vs packages, I may be conflating the two concepts. I generally think of them as different builds of a single crate due to how cargo will differentiate builds by saying `compiled \`xyz\` (lib)`, `compiled \`xyz\` (bin foo)`, etc. – Locke Sep 24 '22 at 22:59
  • @Locke What do you mean? What makes OPs setup "weird"? It is the suggested layout (see https://doc.rust-lang.org/cargo/guide/project-layout.html). What makes the other solutions "better"? Please mark your opinions as such. – Niklas Mohrin Sep 24 '22 at 23:01
  • I don't think you are conflicting concepts, you are just using wrong terminology. The two are not "different builds of a single crate", they are just two crates that happen to have the same name but are of different kind. – Niklas Mohrin Sep 24 '22 at 23:04
  • I was not calling OPs setup weird. I was referring to the concept of a crate depend on itself via `dev-dependency` or similar. I'm also not advocating for the usage of `include!`. Personally, I prefer to separate my binaries and libraries into separate crates within a workspace where applicable to physically separate the two builds. It is rare for me to see the design pattern you were referring to so I do not have as much experience with it. However, I don't particularly like how breaks the assumption that the folder structure more-or-less emulates the structure of the project. – Locke Sep 24 '22 at 23:21
  • This answer is incorrect. **It is not necessary** for a package to have a dependency on itself or do anything else “weird” to have a binary crate depend on a library crate in the same package. In fact, Cargo creates the dependency implicitly. All you need to do is refer to the library by name from the binary's code (`use m::a::f;`) – Kevin Reid Sep 25 '22 at 03:09