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

nz_*_*_21 5 module rust

我有4个文件:

main.rs

mod bar;

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

lib.rs

pub mod foo;
pub mod bar;

Run Code Online (Sandbox Code Playgroud)

foo.rs

pub fn say_foo() {

}

Run Code Online (Sandbox Code Playgroud)

bar.rs

use crate::foo;

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

Run Code Online (Sandbox Code Playgroud)

运行时cargo run出现错误消息:

mod bar;

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

有人可以向我解释如何解决此问题吗?更广泛地说:当存在a main.rs和a 时,模块查找如何工作lib.rs?我觉得很混乱

编辑:添加mod foomain.rs解决此问题。但是我不明白这一点,给我的印象lib.rs是,这个地方“暴露”了我所有的模块?为什么也必须在其中声明模块main.rs

我的Cargo.toml

pub mod foo;
pub mod bar;

Run Code Online (Sandbox Code Playgroud)

L.Y*_*Sim 24

简而言之,Rust 官方书是这样说的:

如果一个包包含src/main.rsand src/lib.rs,它有两个包:一个库和一个二进制文件,两者都与包同名。

此外,Rust 参考文献说:

crate 解析相对于当前 crate 的路径

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

现在在你的代码示例中,如果你想要编译你必须 mod bar;src/main.rs. 否则,您将声明它bar是两个板条箱中的模块。

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

pub mod foo;
pub mod bar;
Run Code Online (Sandbox Code Playgroud)

bar现在将是src/lib.rs's crate 中的一个模块,因此cratein 中的限定符bar.rs将引用src/lib.rs's hello-worldcrate,这就是您想要的。


还有一件事,如果您想访问src/lib.rsfrom中公开的项目src/main.rs,您必须按照@zrzka 所说的去做,即命名两者src/lib.rssrc/main.rs共享的 crate 的名称。例如,在您名为 的项目中hello-world

use hello_world::foo;
fn main() {
    foo::say_foo();
}
Run Code Online (Sandbox Code Playgroud)

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

然而,导入行为似乎并没有以其他方式工作。即,如果您在 中声明了一些公共模块src/main.rssrc/lib.rs即使您指定了 crate 的名称,也无法将其导入到crate 中。我找不到描述此行为的文档,但通过在 Rust 1.37.0 中对其进行测试,情况似乎确实如此。


zrz*_*zka 7

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"
Run Code Online (Sandbox Code Playgroud)

src/main.rs:

[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
Run Code Online (Sandbox Code Playgroud)
fn main() {
    println!("Hallo, Rust here!")
}
Run Code Online (Sandbox Code Playgroud)

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"
Run Code Online (Sandbox Code Playgroud)

src/lib.rs:

$ cargo run
Hallo, Rust here!
Run Code Online (Sandbox Code Playgroud)
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
Run Code Online (Sandbox Code Playgroud)

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"
Run Code Online (Sandbox Code Playgroud)

src/lib.rs:

pub fn foo() {
    println!("Hallo, Rust library here!")
}
Run Code Online (Sandbox Code Playgroud)

src/main.rs:

$ cargo run
error: a bin target must be available for `cargo run`
Run Code Online (Sandbox Code Playgroud)

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"
Run Code Online (Sandbox Code Playgroud)

Same package layout

A library part

src/lib.rs:

[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
Run Code Online (Sandbox Code Playgroud)

src/foo.rs:

pub const GREETING: &'static str = "Hallo, Rust library here!";
Run Code Online (Sandbox Code Playgroud)

src/bar.rs:

use hallo::GREETING;

fn main() {
    println!("{}", GREETING);
}
Run Code Online (Sandbox Code Playgroud)

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:

[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

src/utils/foo.rs:

pub fn say_foo() {
    println!("Foo");
}
Run Code Online (Sandbox Code Playgroud)

src/utils/bar.rs:

use crate::foo;

pub fn bar() {
    foo::say_foo();
}
Run Code Online (Sandbox Code Playgroud)

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

src/main.rs:

use hallo::bar::bar;

fn main() {
    bar();
}
Run Code Online (Sandbox Code Playgroud)

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"
Run Code Online (Sandbox Code Playgroud)

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"
Run Code Online (Sandbox Code Playgroud)

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"
Run Code Online (Sandbox Code Playgroud)

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"
Run Code Online (Sandbox Code Playgroud)

Documentation

  • 你的意思是“use crate::foo;”?为什么你认为它是外部参考,其实不是。`crate` 关键字指的是[当前的板条箱](https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-clarity.html#the-crate-keyword-refers -to-the-current-crate),这是库本身。如果您愿意,您可以使用“use super::foo;”。 (3认同)

Cer*_*rus 5

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

当您使用cargo run(或构建二进制文件并显式运行它)时,要使用的入口点是main.rscrate关键字是指二进制文件箱。它甚至没有要知道,有一些是在lib.rs:二进制将把库,因为这将任何其他外部包装箱,而且必须进口,通过extern crate hello_world或例如,use hello_world::foo

但是,当您导入库时,入口点是lib.rs,而cratelibrary crate。在这种情况下,是的,您添加的所有内容都lib.rs暴露在整个 crate 中。

在这种情况下,通常的工作流程是使二进制文件类似于库周围的薄包装 - 在某些极端情况下,它main.rs只会包含类似的内容

use library;
fn main() {
    library::main();
}
Run Code Online (Sandbox Code Playgroud)

整个逻辑(以及所有项目结构)都进入了库箱。原因之一正是您遇到的情况:可能会混淆是否在包中的每个板条箱中导入此具体模块。

  • 这个答案非常有帮助。换句话说,您可以想象 1. `lib.rs` 实际上称为 `mod.rs`,2. 文件夹 `src` 实际上称为 `name_of_my_crate`,如 `Cargo.toml 的 `name` 字段所示` 的 `[package]` 表,以及 3. `main.rs` 实际上位于您的 crate 之外(因此您必须通过其名称引用您的 crate,而不是通过 `crate::`)。 (3认同)