如何从同一项目的另一个文件中引入一个模块?

303
通过遵循这个指南,我创建了一个Cargo项目。

src/main.rs

fn main() {
    hello::print_hello();
}

mod hello {
    pub fn print_hello() {
        println!("Hello, world!");
    }
}

我使用的程序

cargo build && cargo run

它编译没有错误。现在我正在尝试将主模块分成两个,但无法弄清如何从另一个文件包含一个模块。

我的项目结构如下:

├── src
    ├── hello.rs
    └── main.rs

and the content of the files:

src/main.rs

use hello;

fn main() {
    hello::print_hello();
}

src/hello.rs

mod hello {
    pub fn print_hello() {
        println!("Hello, world!");
    }
}

当我使用cargo build编译时,会得到以下结果。
error[E0432]: unresolved import `hello`
 --> src/main.rs:1:5
  |
1 | use hello;
  |     ^^^^^ no `hello` external crate

我尝试遵循编译器的建议并修改了 main.rs

#![feature(globs)]

extern crate hello;

use hello::*;

fn main() {
    hello::print_hello();
}

但这仍然不能帮助太多,现在我得到了这个:
error[E0463]: can't find crate for `hello`
 --> src/main.rs:3:1
  |
3 | extern crate hello;
  | ^^^^^^^^^^^^^^^^^^^ can't find crate

有没有一个简单的例子,说明如何将当前项目中的一个模块包含到项目的主文件中?

1
可能是[Rust基本导入(包括)]的重复问题。(https://dev59.com/fV8d5IYBdhLWcg3w-mTX) - Levans
与https://dev59.com/YWEh5IYBdhLWcg3wRRuh相关的内容。 - Kelvin
7个回答

453

在您的hello.rs文件中,不需要使用mod hello。任何文件中的代码(除了用于可执行文件的main.rs和用于库的lib.rs)都会自动命名空间化到一个模块中。

要在main.rs中包含来自hello.rs的代码,请使用mod hello;。它会扩展为hello.rs中的代码(与之前完全相同)。您的文件结构保持不变,但您的代码需要稍作更改:

main.rs:

mod hello;

fn main() {
    hello::print_hello();
}

hello.rs:

pub fn print_hello() {
    println!("Hello, world!");
}

11
晚了一步的问题:如果我用"use hello"来代替"mod hello",这样行吗? - Christian Schmitt
52
不,它们是不同的东西。use 只是一个命名空间的概念,而 mod 则将文件引入。 例如,你可以使用 use 来调用 print_hello 函数,而无需在前面加上命名空间前缀。 - Renato Zannon

97
如果您希望使用嵌套模块,现在已经不再需要 强制使用文件mod.rs(尽管仍然支持)。惯用的替代方法是将文件命名为模块名称:
$ tree src
src
├── main.rs
├── my
│   ├── inaccessible.rs
│   └── nested.rs
└── my.rs

main.rs

mod my;

fn main() {
    my::function();
}

my.rs

pub mod nested; // if you need to include other modules

pub fn function() {
    println!("called `my::function()`");
}

Rust 2015

你需要在与模块同名的文件夹中放置一个mod.rs文件。Rust by Example更好地解释了它。

$ tree src
src
├── main.rs
└── my
    ├── inaccessible.rs
    ├── mod.rs
    └── nested.rs

main.rs

mod my;

fn main() {
    my::function();
}

mod.rs

pub mod nested; // if you need to include other modules

pub fn function() {
    println!("called `my::function()`");
}

12
假设我想在nested.rs中使用inaccessible.rs中的内容...我该怎么做? - Heman Gandhi
10
要从除main.rs以外的文件访问同级的.rs文件,可以使用路径属性。因此,在nested.rs的顶部添加以下内容:#[path = "inaccessible.rs"],并在下一行添加:mod inaccessible; - Gardener
3
请将 mod inaccessible; 添加到 my/mod.rs 中,使其成为 my 的子模块。然后可以使用相对路径 super::inaccessible::function()nested.rs 中访问兄弟模块。在此处不需要使用 path 属性。 - artin
我不确定在目录后命名文件的2018年方式更符合惯用法。就我个人而言,我更喜欢旧的方法来保持目录树的清晰,并且许多板条箱也遵循它。 - Chayim Friedman
我认为2015年的例子更有意义,这样想我是不是很奇怪? - Charlie
显示剩余2条评论

52

我真的很喜欢Gardener的回答。我一直在使用这个建议来声明我的模块。

./src
├── main.rs
├── other_utils
│   └── other_thing.rs
└── utils
    └── thing.rs

文件 main.rs

#[path = "utils/thing.rs"] mod thing;
#[path = "other_utils/other_thing.rs"] mod other_thing;

fn main() {
  thing::foo();
  other_thing::bar();
}

文件 utils/thing.rs

pub fn foo() {
  println!("foo");
}

文件 other_utils/other_thing.rs

#[path = "../utils/thing.rs"] mod thing;

pub fn bar() {
  println!("bar");
  thing::foo();
}

1
不得不使用这个“技巧”来重新导出与文件名相同的 fn#[path = "./add_offer.rs"] mod _add_offer; pub use self::_add_offer::add_offer; - Arek Bal
9
作为一个 Rust 新手,如此简单的概念(导入代码的其他部分)需要进行大量研究有些让人不安。而且 #[path..] 语法很丑。 - NullPumpkinException
6
这段话存在误导,#[path = ...] 属性除了在某些晦涩的情况下不应该使用,特别是对于新手而言。每次执行 mod thing 时,都会创建一个新的模块,即使#[path = ...] 把它们指向同一个文件。在这个示例中,声明了两个独立的 thing 模块:crate::thingcrate::other_thing::thing。由于只包含一个函数,这可能不是问题,但如果定义了类型,则会导致当编译器报告“expected mod1::A found mod2::A”时出现混淆。 - kmdreko
4
请使用标准机制。如果你真的想要这种没有中间 mod.rs 文件的文件结构,你可以在 main.rs 中声明它们,例如:mod other_utils { pub mod other_thing; }mod utils { pub mod thing; }。然后,你可以像这样访问它们:crate::other_utils::other_thingcrate::utils::thing。不过请注意保持原意不变,并简化语言以便理解,不提供额外的解释或内容。 - kmdreko

11
在非 main.rs (或 lib.rs) 文件中,如果你想要引入位于同一目录下的文件,则可以使用下面的代码。关键是使用单词 super:: 进行引用。(这是我重新编写了 rodo 的答案,而不使用 path。)
目录树:
src
├── main.rs
├── my.rs
└── my
    ├── a.rs
    └── b.rs

a.rs 包含在 b.rs 中:

文件 src/my/a.rs

pub fn function() {
    println!("src/my/a.rs/function()");
}

文件 src/my/b.rs

use super::b::function;

fn f2() {
    function();
}

文件 src/my.rs

mod a;
mod b;

文件 src/main.rs

mod my;

7

截至2022年

├── src
├── main.rs
├── scripts
│   └── func.rs
└── scripts.rs

如果我想要调用位于scripts文件夹中的func.rs文件中的函数,我需要在与该文件夹同名的根目录下创建一个所谓的“链接”文件(链接文件名和文件夹名称不必相同)。

scripts/func.rs文件的内容如下:

pub fn sayHello(){
    println!("Hello, World!");
}

scripts.rs 文件中,我有以下代码: pub(crate) mod func; 然后在我的 main.rs 文件中,我按如下方式调用了 sayHello() 函数:
mod scripts;
fn main() {
    scripts::func::sayHello();
}

3
截至2022年:你能说明一下(编译器、规范、文档/规范等)的版本吗?也许还要说明测试所用系统(包括版本)。但是请不要包含"Edit:", "Update:"或类似的内容,答案应该看起来像是今天写的。 - Peter Mortensen

4
这个问题有两种情况,到目前为止我看到的答案只涵盖了一种情况:当你有一个`main.rs`,`a.rs`和`b.rs`都在同一个目录下,但你想在`main.rs`中使用`a.rs`或`b.rs`中的函数。但我还没有看到有人涵盖相反的情况,所以我会在这里解释一下。
假设在同一个目录下有`main.rs`,`a.rs`和`b.rs`,而且你想在A中使用B的函数,或者在B中使用A的函数
在`main.rs`中:
mod a;
mod b;

use crate::a::*;
use crate::b::*;

// To call a function from a.rs, do:
a::my_function_from_a();

a.rs
use crate::b;

// To call a function from b.rs, do:
super::b::my_function_from_b();

super 关键字在 a.rs 中是魔法发生的地方。令人讨厌的是,其行为与 Ruby 版本的 super 不同。

如果你不使用 super 关键字,你将遇到可怕的错误:[E0433] 使用未声明的 crate 或模块


如果我在main.rs中没有使用mod b;,为什么我需要它? - undefined
@Velkan 如果你从 b.rs 文件中调用一个函数的话,就会出现这种情况。 - undefined
是的,我在a.rs文件中调用了b.rs文件中的一个函数,而不是在main.rs中调用。为什么我需要关心main.rs呢?它并没有使用b.rs中的任何内容。 - undefined

-8

解决这个问题的方法相对简单,您需要将mod添加到main.rs文件中,以便在整个文件树中都可以访问它。


3
这个答案没有提供任何现有答案所没有的内容,也没有提到需要从模块文件中删除“mod”。 - Chayim Friedman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接