Rust modules confusion when there is main.rs and lib.rs

I have 4 files:

main.rs

mod bar;

fn main() {
    let v = vec![1, 2, 3];
    println!("Hello, world!");
}

lib.rs

pub mod foo;
pub mod bar;

foo.rs

pub fn say_foo() {

}

bar.rs

use crate::foo;

fn bar() {
    foo::say_foo();
}

When I run cargo run I get an error saying:

error[E0432]: unresolved import `crate::foo`
 --> src/bar.rs:1:5
  |
1 | use crate::foo;
  |     ^^^^^^^^^^ no `foo` in the root

Could someone explain to me how to fix this? A bit more broadly: how does module lookup work when there’s a main.rs and a lib.rs?

Edit: Adding mod foo to main.rs fixes the issue. But I don’t understand this — I was under the impression the lib.rs was the place that “exposed” all of my modules? Why do I have to declare the module in main.rs as well?

My Cargo.toml:

[package]
name = "hello-world"
version = "0.1.0"
authors = ["[email protected]>"]
edition = "2018"

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

[dependencies]

2

Let’s start from the beginning. Look at the Package Layout chapter in The Cargo Book. As you can see, your package can contain lot of stuff:

  • a binary (something you can run) or multiple binaries,
  • a single library (shared code),
  • example(s),
  • benchmark(s),
  • integration tests.

Package layout

Not all of the possibilities are listed here, just the binary / library combinations.

A binary

This is an example of a package with single binary. Entry point is the main function in the src/main.rs.

Cargo.toml:

[package]
name = "hallo"
version = "0.1.0"
edition = "2018"

src/main.rs:

fn main() {
    println!("Hallo, Rust here!")
}
$ cargo run
Hallo, Rust here!

A library

This is an example of a package with a library. Libraries don’t have entry points, you can’t run them. They’re used for functionality sharing.

Cargo.toml:

[package]
name = "hallo"
version = "0.1.0"
edition = "2018"

src/lib.rs:

pub fn foo() {
    println!("Hallo, Rust library here!")
}
$ cargo run
error: a bin target must be available for `cargo run`

Do you see anything in the Cargo.toml file about a binary or a library? No. The reason is that I’ve followed the Package Layout and the cargo knows where to look for things.

A binary and a library

This is an example of a package with a binary and a library.

Cargo.toml:

[package]
name = "hallo"
version = "0.1.0"
edition = "2018"

src/lib.rs:

pub const GREETING: &'static str = "Hallo, Rust library here!";

src/main.rs:

use hallo::GREETING;

fn main() {
    println!("{}", GREETING);
}

Same question, do you see anything in the Cargo.toml file about a binary or a library? No.

This package contains two things:

  • a binary (root src/main.rs, entry point src/main.rs::main),
  • a library (root src/lib.rs, shared code).

A library can be referenced from the binary via use hallo::... where the hallo is this package name (Cargo.toml -> [package] -> name).

Your problem

Cargo.toml:

[package]
name = "hallo"
version = "0.1.0"
edition = "2018"

Same package layout

A library part

src/lib.rs:

pub mod bar;
pub mod foo;

src/foo.rs:

pub fn say_foo() {
    println!("Foo");
}

src/bar.rs:

use crate::foo;

pub fn bar() {
    foo::say_foo();
}

crate refers to src/lib.rs, because we’re in the context of our library here.

Treat it as a standalone unit and refer to it via use hallo::...; from the outside world.

A binary part

src/main.rs:

use hallo::bar::bar;

fn main() {
    bar();
}

Here we’re just using our library.

Without a library

Same code, but lib.rs was renamed to utils.rs and (foo|bar).rs files were moved to the src/utils/ folder.

src/utils.rs:

pub mod bar;
pub mod foo;

src/utils/foo.rs:

pub fn say_foo() {
    println!("Foo");
}

src/utils/bar.rs:

use super::foo;
// or use crate::utils::foo;

pub fn bar() {
    foo::say_foo();
}

We can use crate here as well, but because we’re in the context of our binary, the path differs.

src/main.rs:

use utils::bar::bar;

mod utils;

fn main() {
    bar();
}

Here we just declared another module (utils) and we’re using it.

Summary

Cargo.toml content:

[package]
name = "hallo"
version = "0.1.0"
edition = "2018"

If there’s a src/main.rs file, you’re basically saying this:

[package]
name = "hallo"
version = "0.1.0"
edition = "2018"

[[bin]]
name = "hallo"
src = "src/main.rs"

If there’s a src/lib.rs file, you’re basically saying this:

[package]
name = "hallo"
version = "0.1.0"
edition = "2018"

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

If there’re both of them, you’re basically saying this:

[package]
name = "hallo"
version = "0.1.0"
edition = "2018"

[[bin]]
name = "hallo"
path = "src/main.rs"

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

Documentation

  • Package Layout
  • The Manifest Format
  • Managing Growing Projects with Packages, Crates, and Modules

9

In short the official Rust book has this to say:

If a package contains src/main.rs and src/lib.rs, it has two crates: a library and a binary, both with the same name as the package.

Furthermore the Rust reference says this:

crate resolves the path relative to the current crate

So there are actually two crates in your project, and to which crate the crate qualifier resolves to depends on where you call it.

Now in your code example, if you want things to compile you have to remove mod bar; from src/main.rs. Otherwise you’ll be declaring that bar is a module within two crates.

After you remove that, then because in src/lib.rs you had:

pub mod foo;
pub mod bar;

bar would now be a module within src/lib.rs‘s crate, so the crate qualifier in bar.rs would then refer to src/lib.rs‘s hello-world crate, which is what you want.


One more thing, if you wanted to access items that are exposed in src/lib.rs from src/main.rs, you have to do as @zrzka said, which is to name the name of the crate that both src/lib.rs and src/main.rs share. For example, in your project which is named hello-world:

use hello_world::foo;
fn main() {
    foo::say_foo();
}

is how you import the foo module declared in src/lib.rs into src/main.rs.

However it does appear that the importing behavior doesn’t work the other way. I.e. if you declare some public module in src/main.rs, you can’t import it into the src/lib.rs crate even when you specify the name of the crate. I couldn’t find documentation describing this behavior but by testing it in Rust 1.37.0, it does appear to be the case.

The lib.rs and main.rs files are two independent entry points for your package.

When you use cargo run (or build the binary and run it explicitly), the entry point to be used is main.rs, and the crate keyword refer to the binary crate. It doesn’t even have to know that there is something in lib.rs: the binary will treat the library as it would any other external crate, and it must be imported, through extern crate hello_world or, for example, use hello_world::foo.

When you import the library, however, the entry point is lib.rs, and the crate is the library crate. In this case, yes, all that you’ve added to lib.rs is exposed to the whole crate.

The usual worksflow in this case is to make the binary something like a thin wrapper around the library – in some extreme cases the main.rs would only contain something like

fn main() {
    library::main();
}

and the whole logic (and all the project structure) goes into the library crate. One of the reasons is exactly what you’ve run into: the possible confusion whether this concrete module is imported in each crate in the package.

2

(No new info here, but visualized)

(NOTE: my_lib is the crates name, as it appears in Cargo.toml)

Scenario independent dependent
Dependency Diagram
main.rs mod foo; use my_lib::foo;
Explanation The two separate crate(-entry-point)s main & lib use the same source files, but main does not depend on lib. The project consists mainly of a library, and main merely uses this library. main uses modules in this project through lib (for the most part).
Evaluation Usually not what one wants. This kind of makes the project more modular, and is usually what one imagines/wants to use. It also makes main a kind of integration-test for lib. This way, we are more likely to notice for example, if code in the library that should be accessible from the outside, is not.

1

One more thing: mod defines a new module in your crate, whether a binary crate or library crate; while use only brings the module into the current scope.

In your example, use crate::foo in bar.rs tries to bring a module named foo under crate root into scope. But because there is no mod foo in main.rs, the foo module is not part of the binary crate.

Best practice to have a bin project with name app if you want unit tests. lib.rs generates a lib crate, main.rs generate a bin crate, the bin crate just calls the lib crate.

  • lib.rs
pub mod my_module;
  • main.rs
use app::my_module::run;

fn main() {
   run();
}

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật