刚接触Rust遇到一堆新概念,特别是package, crate, mod 这些,特别迷糊,记录一下

一、pakcage与crate

当我们用cargo 创建一个新项目时,默认就创建了一个package,参考下面的截图:

这样就生成了一个名为demo_1的package,另外也创建1个所谓的binary crate,当然也可以加参数 --lib生成library的crate

然后在crate里,又可以创建一堆所谓的mod(模块),因此整体的关系,大致象下面这张图:

即:

  • 1个Package里,至少要有1种Crate(要么是Library Crate,要么是Binary Crate)
  • 1个Package里,最多只能有1个Library Crate
  • 1个Package里,可以有0或多个Binary Crate
  • 1个Crate里,可以创建0或多个mod(后面还会详细讲mod)

二、crate的入口

通常在创建项目后,会默认生成src/main.rs,里面有1个main方法:

(base) ➜  code tree demo_1
demo_1
├── Cargo.toml
└── src
└── main.rs

main.rs的内容:

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

这个就是crate运行时的入口函数,前面我们提过,1个package里,允许有1个library crate和多个binary crate,我们弄个复杂点的场景:

(base) ➜  demo_1 git:(master) ✗ tree
.
├── Cargo.lock
├── Cargo.toml
└── src
├── lib.rs
├── main.rs
└── main2.rs

在src里,再加2个文件lib.rs及main2.rs,内容如下:

lib.rs

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

main2.rs

fn main(){
demo_1::foo();
println!("hello 2");
}

同时把main.rs里也加一行demo_1::foo(),让它调用lib.rs里的foo()方法

fn main() {
demo_1::foo();
println!("Hello, world!");
}

看上去,我们有2个main入口函数了,运行一下看看结果如何:

(base) ➜  demo_1 git:(master) ✗ cargo run
Compiling demo_1 v0.1.0 (/Users/jimmy/code/demo_1)
Finished dev [unoptimized + debuginfo] target(s) in 0.70s
Running `target/debug/demo_1`
foo in lib
Hello, world!

从最后2行的输出来看,运行的是main.rs中的方法,即:main2.rs中的main函数,并未识别成入口,继续折腾,在src下创建目录bin,然后把main.rs以及main2.rs都移动到bin目录

(base) ➜  demo_1 git:(master) ✗ tree
.
├── Cargo.lock
├── Cargo.toml
└── src
├── bin
│ ├── main.rs
│ └── main2.rs
└── lib.rs

然后再运行: 

(base) ➜  demo_1 git:(master) ✗ cargo run
error: `cargo run` could not determine which binary to run. Use the `--bin` option to specify a binary, or the `default-run` manifest key.
available binaries: main, main2

这次提示不一样了,大意是说有2个入口main, main2,不知道该运行哪一个,需要加参数明确告诉cargo,加1个参数 --bin main2

(base) ➜  demo_1 git:(master) ✗ cargo run --bin main2
Compiling demo_1 v0.1.0 (/Users/jimmy/code/demo_1)
Finished dev [unoptimized + debuginfo] target(s) in 0.62s
Running `target/debug/main2`
foo in lib
hello 2

这样就可以了

三、 mod

3.1 定义mod

把main.rs里加点代码:

mod a {
pub fn foo_a_1() {
println!("foo_a_1");
} fn foo_a_2(){
println!("foo_a_2");
} mod b {
pub fn foo_b() {
foo_a_2();
println!("foo_b");
}
}
} fn main() {
a::foo_a_1();
a::foo_a_2();
a::b::foo_b();
}

解释一下:

  • 用mod关键字,定义了模块a,然后里面还嵌套了模块b
  • 然后在main方法里,尝试调用a模块的方法,以及其子模块b中的方法

编译一下,会发现各种报错:

-----------------------------------------------------

(base) ➜ demo_1 git:(master) ✗ cargo build
Compiling demo_1 v0.1.0 (/Users/jimmy/code/demo_1)
error[E0425]: cannot find function `foo_a_2` in this scope
--> src/bin/main.rs:12:13
|
12 | foo_a_2();
| ^^^^^^^ not found in this scope
|
help: consider importing this function
|
11 | use crate::a::foo_a_2;
|

error[E0425]: cannot find function `foo_a` in module `a`
--> src/bin/main.rs:19:8
|
19 | a::foo_a();
| ^^^^^ not found in `a`

error[E0603]: module `b` is private
--> src/bin/main.rs:20:8
|
20 | a::b::foo_b();
| ^ private module
|
note: the module `b` is defined here
--> src/bin/main.rs:10:5
|
10 | mod b {
| ^^^^^

Some errors have detailed explanations: E0425, E0603.
For more information about an error, try `rustc --explain E0425`.
error: could not compile `demo_1` due to 3 previous errors

-----------------------------------------------------

从提示上看,主要是private的问题:

  • 默认情况下Rust里的函数以及模块,都是private作用域的,外界无法访问,所以要改成pub

修改一下:

mod a {
pub fn foo_a_1() {
println!("foo_a_1");
} //修改1:加pub
pub fn foo_a_2(){
println!("foo_a_2");
} //修改2:加pub
pub mod b {
pub fn foo_b() {
//修改3:调用父mod的方法,要加super关键字
super::foo_a_2();
println!("foo_b");
}
}
} fn main() {
a::foo_a_1();
a::foo_a_2();
a::b::foo_b();
}

再运行:

(base) ➜  demo_1 git:(master) ✗ cargo run
Compiling demo_1 v0.1.0 (/Users/jimmy/code/demo_1)
Finished dev [unoptimized + debuginfo] target(s) in 0.33s
Running `target/debug/main`
foo_a_1
foo_a_2
foo_a_2
foo_b

正常了,但是这里可能有同学会疑问:mod a不也没加pub关键字吗,为啥main能正常调用?可以先记一条规则 :如果模块x与main方法在一个.rs文件中,且x处于最外层,main方法可以调用x中的方法。

再微调下代码:

mod a {
//修改:去掉pub
fn foo_a_2(){
println!("foo_a_2");
} pub mod b {
pub fn foo_b() {
super::foo_a_2();
}
}
} fn main() {
a::b::foo_b();
}

再次运行:

(base) ➜  demo_1 git:(master) ✗ cargo run
Compiling demo_1 v0.1.0 (/Users/jimmy/code/demo_1)
Finished dev [unoptimized + debuginfo] target(s) in 0.42s
Running `target/debug/main`
foo_a_2

疑问:父模块mod a中的foo_a_2没加pub,也就是默认private,为啥子模块b能调用?又是一条规则 :子模块可以调用父模块中的private函数,但是反过来是不行的 (通俗点讲:老爸的钱,就是儿子的钱,但是儿子的钱,除非儿子主动给老爸,否则还是儿子的!想必Rust的设计者们,深知“父爱如山”的道理)。

mod a {

    pub fn foo_a_1(){
//这样是不行的,因为foo_b_2是private
b::foo_b_2();
} //修改:去掉pub
fn foo_a_2(){
println!("foo_a_2");
} pub mod b {
pub fn foo_b() {
super::foo_a_2();
} fn foo_b_2(){
println!("foo_b_2");
}
}
} fn main() {
a::foo_a_1();
a::b::foo_b();
}

这样会报错。

3.2 简化访问路径

前面介绍过,main.rs就是cargo的入口,也可以理解为cargo的根,所以就本文的示例而言:

    a::b::foo_b();
self::a::b::foo_b();
crate::a::b::foo_b();

是等效的,就好比,文件d:\a\b\1.txt,如果我们当前已经在d:\根目录下,
a\b\1.txt
d:\a\b\1.txt
.\a\b\1.txt
都能访问。

用全路径crate::a::b::foo_b()虽然能访问,但是代码看着太啰嗦了,可以用use来简化:

mod a {
fn foo_a_2(){
println!("foo_a_2");
} pub mod b {
pub fn foo_b() {
super::foo_a_2();
}
}
} use crate::a::b::foo_b; fn main() {
use crate::a::b::foo_b as x;
foo_b();
x();
}

运行效果一样:

(base) ➜  demo_1 git:(master) ✗ cargo run
Compiling demo_1 v0.1.0 (/Users/jimmy/code/demo_1)
Finished dev [unoptimized + debuginfo] target(s) in 0.39s
Running `target/debug/main`
foo_a_2
foo_a_2

从上面的示例可以看到:

  • use即可以在函数体内,也可以在函数外
  • 当2个模块的函数有重名时,可以用use .. as .. 来取个别名 

3.3 将mod拆分到多个文件

如上图,把mod a与b,分拆到a.rs, 及b.rs,与main.rs放在同1目录。注意main.rs的首二行:

mod a;
mod b;

与常规mod不同的是,mod x后,并没有{...}代码块,而是;号,rust会在同级目录下,默认去找x.rs,再来看main方法:

fn main() {
a::a::foo_a_2();
b::b::foo_b();
}

为何这里是a::a:: 连写2个a? 因为最开始声明mod a; 这里面已有1个模块a,而a.rs里首行,又定义了1个pub mod a,所以最终就是a::a::

如果mod太多,都放在一个目录下,也显得很乱,可以建个目录,把mod放到该到目录下:

(base) ➜  demo_1 git:(master) ✗ tree
.
├── Cargo.lock
├── Cargo.toml
└── src
├── abc
│ ├── a.rs
│ ├── b.rs
│ └── mod.rs
└── main.rs

这时要在该目录下,新增1个mod.rs,用于声明该目录下有哪些模块

pub mod a;
pub mod b;

然后b.rs中引用a模块时,路径也要有所变化:

pub mod b {
use crate::abc::a::a::foo_a_2;
pub fn foo_b() {
foo_a_2();
}
}

main.cs里也要相应调整:

mod abc;

fn main() {
abc::a::a::foo_a_2();
abc::b::b::foo_b();
}

目录abc,本身就视为1个mod,所以main.rs里的mod abc; 就是声明abc目录为1个mod,然后再根据abc/mod.rs,进一步找到a, b二个mod 

Rust中的代码组织:package/crate/mod的更多相关文章

  1. [易学易懂系列|rustlang语言|零基础|快速入门|(16)|代码组织与模块化]

    [易学易懂系列|rustlang语言|零基础|快速入门|(16)|代码组织与模块化] 实用知识 代码组织与模块化 我们知道,在现代软件开发的过程中,代码组织和模块化是应对复杂性的一种方式. 今天我们来 ...

  2. Rust 中的继承与代码复用

    在学习Rust过程中突然想到怎么实现继承,特别是用于代码复用的继承,于是在网上查了查,发现不是那么简单的. C++的继承 首先看看c++中是如何做的. 例如要做一个场景结点的Node类和一个Sprit ...

  3. 【译】理解Rust中的Futures(二)

    原文标题:Understanding Futures in Rust -- Part 2 原文链接:https://www.viget.com/articles/understanding-futur ...

  4. PHP中PSR-[0-4]代码规范

    PHP-FIG 在说啥是PSR-[0-4]规范的之前,我觉得我们有必要说下它的发明者和规范者:PHP-FIG,它的网站是:www.php-fig.org.就是这个联盟组织发明和创造了PSR-[0-4] ...

  5. GO1.6语言学习笔记2-安装配置及代码组织

    一.关于GO开发环境的安装和配置        在linux环境中安装编译好的go安装包,参考官方指南的步骤一步步走下来就可以了.需要注意的是以下几个环境变量的配置:        GOROOT - ...

  6. 《Java虚拟机原理图解》1.5、 class文件中的方法表集合--method方法在class文件中是怎样组织的

    0. 前言 了解JVM虚拟机原理是每一个Java程序员修炼的必经之路.但是由于JVM虚拟机中有很多的东西讲述的比较宽泛,在当前接触到的关于JVM虚拟机原理的教程或者博客中,绝大部分都是充斥的文字性的描 ...

  7. 《Java虚拟机原理图解》1.4 class文件中的字段表集合--field字段在class文件中是怎样组织的

    0.前言 了解JVM虚拟机原理是每一个Java程序员修炼的必经之路.但是由于JVM虚拟机中有很多的东西讲述的比较宽泛,在当前接触到的关于JVM虚拟机原理的教程或者博客中,绝大部分都是充斥的文字性的描述 ...

  8. Rust 中的类型转换

    1. as 运算符 as 运算符有点像 C 中的强制类型转换,区别在于,它只能用于原始类型(i32 .i64 .f32 . f64 . u8 . u32 . char 等类型),并且它是安全的. 例 ...

  9. python代码组织及模块使用

    python代码组织 python和其他编程语言一样,采用包管理代码,一个包中可包含许多模块. 使用模块最大的好处是大大提高了代码的可维护性.其次,编写代码不必从零开始.当一个模块编写完毕,就可以被其 ...

  10. 【译】理解Rust中的Futures (一)

    原文标题:Understanding Futures In Rust -- Part 1 原文链接:https://www.viget.com/articles/understanding-futur ...

随机推荐

  1. 俩天完美复刻DeepWiki,并且免费开源!

    俩天完美复刻DeepWiki,并且免费开源! 大家好!今天非常高兴为大家介绍KoalaWiki项目 - 这是我们团队花费两天时间完美复刻一个免费开源的AI驱动代码知识库系统,可以说是DeepWiki的 ...

  2. redis没有启动会报什么错???

    以下是报错信息,应该是根据项目不同,具体的不太一样,但我们能清晰的看到一句 加红,加大,加粗 Caused by: redis.clients.jedis.exceptions.JedisConnec ...

  3. 4G模块——大夏龙雀DX-CT511-A使用记录

    4G模块--大夏龙雀DX-CT511-A使用记录 加回车换行 115200波特率 重启: AT+RESET 6.关闭HTTP服务: AT$HTTPCLOSE 关闭网路 AT+NETCLOSE 1.TC ...

  4. TDesign腾讯高保真Axure RP中后台交互模板及元件组件库

    TDesign腾讯Axure RP中后台交互模板部件及元件组件库素材基于腾讯TDesign素材库,进行二次创作,并非官网的免费静态版.具体内容,可以看右侧的预览按钮,确认内容. 在线演示及下载:htt ...

  5. 进程间通信-POSIX 消息队列

    POSIX 消息队列 POSIX 消息队列可以认为是一个消息链表.进程(线程)可以往里写消息,也可以从里面取出消息.可以在不相关的进程之间发送和接收数据. 创建(打开)消息队列-mq_open()函数 ...

  6. C#实现MergeSort算法

    public class MergeSortLearn { /// <summary> /// 分治递归 /// </summary> /// <param name=& ...

  7. Python中的模块包

    dir0/dir1/dir2/mod.py,dir0必须在环境变量中,可以import dir1,import dir1.dir2.mod.但在python3.3之前,dir1和dir2下必须存放一个 ...

  8. dev c++基础操作

    文章目录 改变字体大小&改变背景色 最终效果 调节字体大小 调节背景色 常用快捷键 调试时粘贴测试数据 改变字体大小&改变背景色 最终效果 调节字体大小 Tools -> Edi ...

  9. SQL注入入门实例讲解(NewsCenter

    今天写了个sql注入的题目(也是跟着教程做的hhh),过程中有些心得体会,想着写篇博客总结一下. 笔者也是初学者,如有哪里讲错了,欢迎大佬在评论区点出 题目来源:攻防世界web进阶区,NewsCent ...

  10. STM32在使用Clion平台开发时调试失败 SystemClock_Config 返回 HAL_ERROR

    问题记录 在尝试使用Clion在STM32平台上开发调试时,需要通过OpenOCD结合ST-Link等调试器进行烧录和调试.但通过STM32CubeMX生成代码后,发现出现以下现象: 程序能够正常编译 ...