当存在 main.rs 和 lib.rs 时,生锈模块会产生混淆

我有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();
}


当我运行 cargo run时,我得到一个错误:

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

有人能给我解释一下怎么修吗?更广泛一点: 当有一个 main.rs和一个 lib.rs时,模块查找是如何工作的?

编辑: 将 mod foo添加到 main.rs修复了这个问题。但是我不明白——我以为 lib.rs是“暴露”我所有模块的地方?为什么我还必须在 main.rs中声明模块?

我的 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]
66684 次浏览

lib.rsmain.rs文件是您的包的两个独立的入口点。

使用 cargo run(或构建二进制文件并显式运行它)时,要使用的入口点是 main.rs,而 crate关键字引用 二进制板条箱。它甚至不需要知道在 lib.rs中有什么东西: 二进制文件将像对待其他任何外部机箱一样对待这个库,并且它必须通过 extern crate hello_world或者,例如,use hello_world::foo来导入。

但是,当您导入库时,入口点是 lib.rs,而 crate图书箱。在这种情况下,是的,所有你已经添加到 lib.rs是暴露在整个板条箱。

在这种情况下,通常的工作流程是将二进制文件变成类似于库周围的一个薄包装器——在某些极端情况下,main.rs只包含类似于

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

整个逻辑(和所有的项目结构)进入库箱。其中一个原因正是您遇到的问题: 这个具体的模块是否被导入到包中的每个板条箱中可能会引起混淆。

我们从头开始吧。看看 货物登记簿中的 包装设计章。正如你所看到的,你的包可以包含很多东西:

  • 二进制文件(你可以运行的东西)或者多个二进制文件,
  • 单个库(共享代码) ,
  • 例子、,
  • 基准、,
  • 综合测试。

包装布局

这里并没有列出所有的可能性,只是列出了二进制/库的组合。

二进制

这是一个包含单个二进制文件的例子。入口点是 src/main.rs中的 main函数。

返回文章页面

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

返回文章页面

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

图书馆

这是一个带有库的包示例。图书馆没有入口点,你不能运行它们。它们用于功能共享。

返回文章页面

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

返回文章页面

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

Cargo.toml文件中有没有看到任何关于二进制文件或库的内容?没有。原因是我已经跟随了 包装设计cargo知道在哪里寻找东西。

一个二进制文件和一个库

这是一个包含二进制文件和库的示例。

返回文章页面

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

返回文章页面

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

返回文章页面

use hallo::GREETING;


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

同样的问题,在 Cargo.toml文件中有没有看到任何关于二进制文件或库的内容? 没有。

该软件包包含两个内容:

  • 一个二进制文件(根 src/main.rs,入口点 src/main.rs::main) ,
  • 库(根 src/lib.rs,共享代码)。

库可以通过 use hallo::...从二进制文件中引用,其中 hallo是这个包的名称(Cargo.toml-> [package]-> name)。

你的问题

返回文章页面

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

一样的包装布局

图书馆的一部分

返回文章页面

pub mod bar;
pub mod foo;

返回文章页面

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

返回文章页面

use crate::foo;


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

crate指的是 src/lib.rs,因为我们在我们的库的上下文中。

把它当作一个独立的单元,并通过 use hallo::...;从外部世界引用它。

二进制零件

返回文章页面

use hallo::bar::bar;


fn main() {
bar();
}

在这里我们只是使用我们的图书馆。

没有图书馆

同样的代码,但是 lib.rs被重命名为 utils.rs,而 (foo|bar).rs文件被移动到 src/utils/文件夹。

返回文章页面

pub mod bar;
pub mod foo;

返回文章页面

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

返回文章页面

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


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

我们在这里也可以使用 crate,但是因为我们处于二进制文件的上下文中,所以路径是不同的。

返回文章页面

use utils::bar::bar;


mod utils;


fn main() {
bar();
}

这里我们刚刚声明了另一个模块(utils) ,我们正在使用它。

摘要

Cargo.toml内容:

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

如果有一个 src/main.rs文件,基本上就是说:

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


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

如果有一个 src/lib.rs文件,基本上就是说:

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


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

如果两者都有,你基本上就是在说:

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


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


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

文件

简而言之,Rust 的官方手册是这么说的:

如果一个包包含 src/main.rssrc/lib.rs,那么它有两个板条箱: 一个库和一个二进制文件,它们的名字都与包相同。

此外,Rust 的参考文献表示:

crate解析相对于当前板条箱的路径

因此,在您的项目中实际上有两个板条箱,crate限定符解析到哪个板条箱取决于您调用它的位置。

现在在您的代码示例中,如果您希望从 src/main.rs编译 你得把它取出来mod bar;。否则,您将声明 bar是两个板条箱中的一个模块。

在你删除它之后,因为在 src/lib.rs中你有:

pub mod foo;
pub mod bar;

bar现在将是 src/lib.rs的板条箱内的一个模块,因此 bar.rs中的 crate限定符将引用 src/lib.rshello-world板条箱,这就是您想要的。


还有一件事,如果你想从 src/main.rs访问在 src/lib.rs中暴露的物品,你必须按照@zrzka 说的那样做,即命名 src/lib.rssrc/main.rs共享的板条箱的名称。例如,在名为 hello-world的项目中:

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

是如何将 src/lib.rs中声明的 foo模块导入到 src/main.rs中。

但是,导入行为似乎并不以另一种方式工作。也就是说,如果你在 src/main.rs中声明了一些公共模块,即使你指定了箱子的名字,你也不能把它导入到 src/lib.rs的箱子中。我找不到描述这种行为的文档,但是通过在 Rust 1.37.0中对其进行测试,似乎确实如此。