简体   繁体   English

当存在 main.rs 和 lib.rs 时,Rust 模块会混淆

[英]Rust modules confusion when there is main.rs and lib.rs

I have 4 files:我有4个文件:

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:当我运行cargo run我收到一条错误消息:

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 ?有点更广泛:如何做模块查找工作时,有一个main.rslib.rs

Edit: Adding mod foo to main.rs fixes the issue.编辑:将mod foo添加到main.rs解决了这个问题。 But I don't understand this -- I was under the impression the lib.rs was the place that "exposed" all of my modules?但我不明白这一点——我的印象是lib.rs是“暴露”我所有模块的地方? Why do I have to declare the module in main.rs as well?为什么我还必须在main.rs声明模块?

My Cargo.toml :我的Cargo.toml

[package]
name = "hello-world"
version = "0.1.0"
authors = ["me@mgail.com>"]
edition = "2018"

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

[dependencies]

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 .入口点是src/main.rsmain函数。

Cargo.toml : Cargo.toml :

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

src/main.rs : 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 : Cargo.toml :

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

src/lib.rs : 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?您是否在Cargo.toml文件中看到有关二进制文件或库的任何内容? No. The reason is that I've followed the Package Layout and the cargo knows where to look for things.不会。原因是我遵循了包裹布局cargo知道去哪里找东西。

A binary and a library一个二进制文件和一个库

This is an example of a package with a binary and a library.这是一个包含二进制文件和库的包示例。

Cargo.toml : Cargo.toml :

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

src/lib.rs : src/lib.rs

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

src/main.rs : 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?同样的问题,您是否在Cargo.toml文件中看到有关二进制文件或库的任何内容? No.不。

This package contains two things:这个包包含两件事:

  • a binary (root src/main.rs , entry point src/main.rs::main ),一个二进制文件(根src/main.rs ,入口点src/main.rs::main ),
  • a library (root src/lib.rs , shared code).一个库(根src/lib.rs ,共享代码)。

A library can be referenced from the binary via use hallo::... where the hallo is this package name ( Cargo.toml -> [package] -> name ).数据库可以从二进制通过引用use hallo::...其中hallo是这个包名称( Cargo.toml - > [package] - > name )。

Your problem你的问题

Cargo.toml : Cargo.toml :

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

Same package layout相同的包装布局

A library part图书馆部分

src/lib.rs : src/lib.rs

pub mod bar;
pub mod foo;

src/foo.rs : src/foo.rs

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

src/bar.rs : 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. crate指的是src/lib.rs ,因为我们在这里处于我们库的上下文中。

Treat it as a standalone unit and refer to it via use hallo::...;将其视为一个独立的单元,并通过use hallo::...;来引用它use hallo::...; from the outside world.来自外部世界。

A binary part二进制部分

src/main.rs : 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.相同的代码,但lib.rs被重命名为utils.rs并且(foo|bar).rs文件被移动到src/utils/文件夹。

src/utils.rs : src/utils.rs

pub mod bar;
pub mod foo;

src/utils/foo.rs : src/utils/foo.rs

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

src/utils/bar.rs : 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.我们也可以在这里使用crate ,但是因为我们在二进制文件的上下文中,所以路径不同。

src/main.rs : src/main.rs :

use utils::bar::bar;

mod utils;

fn main() {
    bar();
}

Here we just declared another module ( utils ) and we're using it.在这里,我们刚刚声明了另一个模块( utils )并且我们正在使用它。

Summary概括

Cargo.toml content: Cargo.toml内容:

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

If there's a src/main.rs file, you're basically saying this:如果有一个src/main.rs文件,你基本上是这样说的:

[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:如果有一个src/lib.rs文件,你基本上是这样说的:

[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文档

In shortthe official Rust book has this to say:简而言之,Rust 官方书是这样说的:

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.如果一个包包含src/main.rssrc/lib.rs ,它有两个 crate:一个库和一个二进制文件,两者都与包同名。

Furthermore the Rust reference says this:此外,Rust 参考文献说:

crate resolves the path relative to the current crate crate解析相对于当前 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.因此,您的项目中实际上有两个 crate,并且crate限定符解析到哪个 crate 取决于您调用它的位置。

Now in your code example, if you want things to compile you have to remove mod bar;现在在你的代码示例中,如果你想要编译你必须删除mod bar; from src/main.rs .来自src/main.rs Otherwise you'll be declaring that bar is a module within two crates.否则,您将声明bar是两个板条箱中的模块。

After you remove that, then because in src/lib.rs you had:删除它之后,因为在src/lib.rs你有:

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. bar现在将成为src/lib.rs的 crate 中的一个模块,因此bar.rscrate限定符将引用src/lib.rshello-world crate,这就是您想要的。


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.还有一件事,如果你想被暴露访问项目src/lib.rssrc/main.rs ,你必须为@zrzka说,这是命名的箱子,这两个的名字做src/lib.rssrc/main.rs共享。 For example, in your project which is named hello-world :例如,在您名为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 .是你如何导入foo中声明模块src/lib.rssrc/main.rs

However it does appear that the importing behavior doesn't work the other way.然而,导入行为似乎并没有以其他方式工作。 Ie 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.也就是说,如果你在src/main.rs声明了一些公共模块,即使你指定了包的名称,你也不能将它导入到src/lib.rs包中。 I couldn't find documentation describing this behavior but by testing it in Rust 1.37.0, it does appear to be the case.我找不到描述此行为的文档,但通过在 Rust 1.37.0 中对其进行测试,情况似乎确实如此。

The lib.rs and main.rs files are two independent entry points for your package.lib.rsmain.rs文件是你的包两个独立的入口点。

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 .当您使用cargo run (或构建二进制文件并显式运行它)时,要使用的入口点是main.rs ,并且crate关键字指的是二进制 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 .它甚至不必知道lib.rs中有什么东西:二进制文件会像对待任何其他外部 crate 一样对待库,并且必须通过extern crate hello_world或例如use hello_world::foo导入它use hello_world::foo

When you import the library, however, the entry point is lib.rs , and the crate is the library crate .但是,当您导入库时,入口点是lib.rs ,而crate库 crate In this case, yes, all that you've added to lib.rs is exposed to the whole crate.在这种情况下,是的,您添加到lib.rs所有lib.rs都暴露在整个 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在这种情况下,通常的工作流程是使二进制文件类似于库周围的薄包装 - 在某些极端情况下, main.rs将只包含类似

use library;
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.原因之一正是您遇到的情况:可能会混淆是否在包中的每个板条箱中导入此具体模块。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM