当前位置:网站首页>Rust 入门指南(modules 和工程结构)
Rust 入门指南(modules 和工程结构)
2022-07-28 17:31:00 【王泰】
Rust 入门指南(modules 和工程结构)
前面
Rust 入门指南(rustup, cargo) Rust 入门指南(crate 包管理)
第一篇文章中,我们讨论了「Rust 的安装」和「使用 cargo 工具创建新项目」。 在这篇文章中,我们将进一步了解 Rust 项目的结构,并深入了解 crates、modules 和 prelude 的概念。
如果你还没有 Rust 环境,
去安装 Rust 或者使用 https://geekcode.cloud 平台,
确保可以创建 rust 项目 :
$ cargo new hello_rust
创建一个新的可执行程序,因此您可以直接运行它:
$ cargo run
cargo 首先编译然后运行:
$ cargo run
“Hello, World!”
OK,接下来我们将讨论:
默认的 Rust 项目结构 main.rs 文件 Rust Modules(文件) Rust Modules和 可见性 Rust Modules(文件夹) 什么是 prelude?
默认的 Rust 结构
Rust 默认基本结构如下,文件夹的结构是不能随意更改的:
hello_rust
- src
- main.rs
- .gitignore
- Cargo.toml
我们可以随时使用 cargo check 命令来检查文件夹结构和 Cargo.toml 文件。 如果出现了错误(比如我将 src 改名成 src1 ),cargo check 会提示:
error: failed to parse manifest at `/Users/geekcode/_working/scratch/hello_rust/Cargo.toml`Caused by:
no targets specified in the manifest
either src/lib.rs, src/main.rs, a [lib] section, or [[bin]] section must be present
例子中必须有一个src/main.rs 文件,因为我们创建的是 binaryapplication。 如果我们创建的是 library(在cargo new 时添加参数--lib ),那么cargo 会为我们创建src/lib.rs。
Cargo.lock 是自动生成的,不能修改。 Cargo 默认初始化一个 Git 存储库,还包含一个 .gitignore。
/target
执行 cargo build 自动创建target 文件夹,并在其中包含构建 artifacts 文件夹(根据不同的配置可能是 debug 或 release 文件夹,默认为 debug)。
如果需要交叉编译到其他平台,那么会增加一个级别文件夹表示目标平台,然后才是构建配置(release 或 debug)。
最后是 main.rs 文件,它是应用程序的入口,内容如下。
main.rs 文件
默认的 main.rs 文件非常简单:
fn main() {
println!("Hello, world!");
}
main() 函数,这是我们应用程序的入口,它只打印“Helo, World!” 到标准输出。
请注意 println! 中的 println 函数是一个 Rust 宏(一种高级 Rust 语法功能),暂时可以忽略细节,现在只要记住它不是一个常规函数。
接下来我们要在main.rs 中愉快地写代码了,先来了解一下 modules 。
Modules
为了代码更加的简洁,我们添加一个 struct。 后面我们会把这段代码从主文件中移出,现在只需将 main.rs 改为:
struct MyStruct {}
fn main() {
let _ms = MyStruct {}; <-- Note the '_'
}
这个 demo 很简单,它清晰的描述 Rust 的模块定义。
注意 _ 前缀变量名:如果定义了不使用的变量,Rust 会有警告。所以我们在变量前使用 _ 前缀,它可以通知编译器这是我们故意的,阻止编译器发出警告。 但在日常开发中我们不建议这么使用。
接下来我们要重构代码,将一个复杂的 struct 移到另一个文件中。 代码保持高内聚低耦合,我们建立了一新的文件 my_struct.rs :
hello_rust
- src
- main.rs
- my_struct.rs
文件必须被添加到 src/ 文件夹下,便编译器才能找到。 文件的命名规范使用 snake_case。
把 struct 声明从 main.rs 中移动到 my_struct.rs 中:
// Contents of my_struct.rs
struct MyStruct {}
构建项目:
$ cargo build
因为 main.rs 删除了 struct 声明,所以显示如下错误:
Compiling hello_rust v0.1.0 (/scratch/hello_rust)
error[E0422]: cannot find struct, variant or union type `MyStruct` in this scope
→ src/main.rs:2:15
|
2 | let _ms = MyStruct {};
|^^^^^^^^ not found in this scope
error: aborting due to previous errorFor more information about this error, try `rustc — explain E0422`. error: could not compile `hello_rust`
Rust 告诉我们它无法找到 struct 的定义。 我们必须明确地声明 modules。 如果我们不声明,Rust 不会主动查找/编译 module。
引入 struct 声明,我们需要修改 main.rs 添加 module 引用,如下所示:
mod my_struct;
fn main() {
let _ms = MyStruct {};
}
在 Rust 中所有文件和文件夹都是 module。 为了在模块中使用代码,我们需要首先使用 mod 语法 import 它。 本质上,这是在“mod my_struct;”语句的位置插入模块中的代码。
再次尝试构建。 等等,这是什么!? 它仍然不起作用……嗯。 我们来看看错误信息:
Compiling hello_rust v0.1.0 (/scratch/hello_rust)
error[E0422]: cannot find struct, variant or union type `MyStruct` in this scope
→ src/main.rs:4:15
|
4 | let _ms = MyStruct {};
|^^^^^^^^ not found in this scope
|
help: consider importing this struct
|
1 |use crate::my_struct::MyStruct;
|
虽然错误是一样的,但现在有一个有用的提示关于添加:
use crate::my_struct::MyStruct;
我们将 main.rs 更改为如下所示:
mod my_struct;
use crate::my_struct::MyStruct;
fn main() {
let _ms = MyStruct {};
}
当使用 mod 语句导入模块时,Rust 自动 为它创建一个模块命名空间(以避免冲突),因此我们无法直接访问我们的 struct 类型。 模块命名空间自动取自文件名(因为在这种情况下 module 是一个文件),因此 **my_struct**::MyStruct; 是use语句的一部分——它来自文件名 my_struct.rs(不带文件扩展名)。
use 语句中的crate:: 部分是因为所有 Rust 项目都是 crate。 Rust 项目可以由多个文件(modules)组成,文件也可以嵌套在文件夹中(也是 modules)。 使用crate::前缀表示来访问 module 树的根目录。
因此,再次查看我们的 main.rs :
mod my_struct; <-- Import the module code, placing
it into the 'my_struct'
namespaceuse
use crate::my_struct::MyStruct; <-- Map the fully qualified (from
the crate root) struct
declaration to just 'MyStruct'
fn main() {
let _ms = MyStruct {}; <-- Yay, we found it! .. or did we?
}
请记住这两点:
必须使用 mod引入 module(文件或文件夹)。use关键字可以方便地将完整限定的类型名称映射到类型名。
Modules - 可见性
我们现在运行上面的 main.rs 会出现新的错误:
Compiling hello_rust v0.1.0 (/scratch/hello_rust)
error[E0603]: struct `MyStruct` is private
→ src/main.rs:2:23
|
2 | use crate::my_struct::MyStruct;
|^^^^^^^^ private struct
|
这告诉我们,虽然我们找到了 struct 声明,但模块的可见性是私有的,因此我们无法在此处访问它。
Rust 中的可见性与 Java 等语言略有不同,需要记住几条规则:
模块 内部的所有内容(即 /src文件夹中的文件或子文件夹)可以访问该模块内的 任何其他内容。模块 外部的所有内容 只能访问该模块的公共成员。
可能看起来很奇怪,但它也有一些非常不错的作用——模块中的私有函数可以用于该模块的单元测试( Rust 将单元测试保留在模块中)。 其次,每个模块都必须声明一个公共接口,定义模块外部可以访问哪些成员。
要公开模块的成员,我们必须添加 pub 关键字。 让我们再次访问我们的 my_struct.rs 文件并将内容替换为:
pub struct MyStruct {} <-- Add the 'pub' keyword
现在我们现在可以成功构建我们这个复杂的程序了 :)
注意:我们可以将 pub 放在大多数声明中,包括 struct、struct 字段、函数、常量等。
Modules - 文件夹
假如我们想将 MyStruct 拆分为多个文件。 当然,要将代码放到一个文件夹中。
Rust 可以以相同的方式(作为 modules)处理文件和文件夹,但有一个重要的区别。
我们创建一个名为 foo/ 的文件夹,我们的 MyStruct 是 foo 功能的一部分。 接下来将文件 my_struct.rs 移动到 /src/foo。 即,新的文件夹结构应该看起来像——
- src/
- main.rs
-foo/
- my_struct.rs
现在修改 main.rs 包含我们的新模块 foo 替换 my_struct:
mod foo; <-- Change the module to match the folder
use crate::foo::MyStruct; <-- Update the namespace to 'foo'
fn main() {
let _ms = MyStruct {};
}
我们现在构建(cargo build),结果出错:
Compiling hello_rust v0.1.0 (/scratch/hello_rust)
error[E0583]:file not found for module `foo`
→ src/main.rs:1:1
|
1 | mod foo;
| ^^^^^^^^
|
= help:to create the module `foo`, create file “src/foo.rs” or “src/foo/mod.rs”
当引用文件夹 module 时,我们使用文件夹名称(就如同对文件 module 一样),但 Rust 需要文件夹中存在一个名为 mod.rs 的文件。
在这种情况下,我们可以简单地将 my_struct.rs 重命名为 mod.rs。
为了完整点我们在 foo/ 文件夹中添加一个文件,其中包含另一个 struct 定义(想象中的名称为 Another):
// Contents of src/foo/another.rs
pub struct Another {} <-- We're going to expose this as public
from the 'foo' module so that we can
use it in main.rs
我们将新模块导入 mod.rs 文件 -
// Contents of src/foo/mod.rs
pub mod another; <-- Add the module import for 'another'
Note the use of 'pub' to expose the
module 'another' as public from the
module 'foo'
pub struct MyStruct {}
最后在 main.rs 中使用我们新的 Another 结构
mod foo;
use crate::foo::MyStruct;
use crate::foo::another::Another; <-- Note that 'another' is a
module within 'foo'
fn main() {
let _ms = MyStruct {};
let _a = Another {}; <-- Using prefix '_' as before
}
看起来有点啰嗦,所以我们接下来介绍 Preludes。
Preludes
修改 foo/ 中的 mod.rs :
mod another; <-- Remove the 'pub' modifier
pub use another::Another; <-- Add a use'ing to map Another directly
into 'foo' and make it public
pub struct MyStruct {}
我们不希望 another模块公开。我们先删除了 pub 关键字,然后用use 将 Another 的完全限定类型映射到 foo 命名空间(因为我们在 foo 模块中)。
最后,修改我们的 main.rs:
mod foo;
use crate::foo::{MyStruct,Another};
fn main() {
let _ms = MyStruct {};
let _a = Another {};
}
请注意,由于我们已经将 Another 的类型名称映射到了 foo 模块中,我们可以使用 use 一次导入多个名称。
这就是 Preludes,Preludes 是一种模式,可以使你公开的所有想要的类型。
main.rs :
mod foo;
mod prelude { <-- Create module inline
pub use crate::foo::{MyStruct,Another};<-- Note the 'pub' here!
}
use crate::prelude::*; <-- Make the types exposed
in the prelude
available
fn main() {
let _ms = MyStruct {};
let _a = Another {};
}
我们还可以将 Preludes 定义为另一个模块(使用 mod)
我们可以像使用其他任何模块一样使用“prelude”模块,例如在 mod.rs 文件中:
mod another;
pub use another::Another;
use crate::prelude::*;
pub struct MyStruct {}
虽然这个 case 其实不需要 preludes 。 但是你可以发现,只要是在 preludes 中声明了的 crate、标准库类型、常量和其他模块,你就可以用use 语句快速的访问到。
通过这个模块还可以发现:
可以使用通配符 ::*从模块中导入所有公共名称可以使用 crate::访问模块树的根目录(即本例中的主模块),并且可以在应用程序的任何位置执行此操作。
总结
Rust 中的模块系统绝对是该语言更令人困惑的点之一。 但是一旦了解了模块是什么(文件、文件夹)以及如何导入它们(mod),如何将名称映射到不同的模块(use),它就开始变得有简单了。
另外最重要的一点是,Rust 的一些文件名已经有了明确的含义(main.rs,lib.rs,mod.rs)不能修改。
Keep rusty!
加入 GeekCode Community 参与定义 Cloud Based IDE

点击【阅读原文】访问 GeekCode
边栏推荐
- Adobe XD web design tutorial
- GPIO port configuration of K60
- 使用百度EasyDL实现明厨亮灶厨师帽识别
- Powerbi time series analysis, prediction and visualization tutorial
- SaltStack之数据系统
- Method of win7 system anti ARP attack
- BM16 删除有序链表中重复的元素-II
- [machine learning] support vector machine classification
- 关于白盒测试,这些技巧你得游刃有余~
- 软件测试开发基础|测开中的几个工具开发实战
猜你喜欢

使用Xilinx MIG验证硬件DDR设计

From Bayesian filter to Kalman filter (I)

C language (high-level) character function and string function + Exercise

智能合约安全——溢出漏洞

软件测试开发基础|测开中的几个工具开发实战
![[image hiding] digital image information hiding system based on DCT, DWT, LHA, LSB, including various attacks and performance parameters, with matlab code](/img/a4/5c5a90508e2f9c6b4f8e234bdfdc9e.png)
[image hiding] digital image information hiding system based on DCT, DWT, LHA, LSB, including various attacks and performance parameters, with matlab code

SQL custom automatic calculation

力扣 1331. 数组序号转换

VIM learning manual

6-20 vulnerability exploitation proftpd test
随机推荐
Pytorch:交叉熵损失(CrossEntropyLoss)以及标签平滑(LabelSmoothing)的实现
Adobe Flash player 34.0.0.92 and available version modification methods (2021-01-23
It is the best tool to evaluate six kinds of map visualization software in three categories
SaltStack系统初始化
6-20漏洞利用-proftpd测试
IMU 加热
Accumulation and development -- the way of commercialization of open source companies
Using CPLEX to solve small-scale energy-efficient FJSP
More loading in applets (i.e. list paging)
Random finite set RFs self-study notes (6): an example of calculation with the formula of prediction step and update step
IMU heating
Wechat official account custom sharing and updateappmessagesharedata are all successful. Why is it a link that is shared?
用于异常检测的Transformer - InTra《Inpainting Transformer for Anomaly Detection》
Asp net MVC web development tutorial
DevCon. Exe export output to the specified file
使用SaltStack自动化部署LNMP
Learn from Li Mu, deep learning - linear regression and basic optimization function
R语言与数据分析实战11-数据的删除
机器学习 --- 模型评估、选择与验证
JDBC simple encapsulation