rust有自己的规则和约定用来组织模块,比如一个包最多可以有一个库crate,任意多个二进制crate、导入文件夹内的模块的两种约定方式... 知道这些约定,就可以快速了解rust的模块系统。

先把一些术语说明一下:

  • 是cargo的一个功能,当执行cargo new xxxx的时候就是创建了一个包。
  • crate是二进制或者库项目。rust约定在Cargo.toml的同级目录下包含src目录并且包含main.rs文件,就是与包同名的二进制crate,如果包目录中包含src/lib.rs,就是与包同名的crate。包内可以有多crate,多个crates就是一个模块的树形结构。如果一个包内同时包含src/main.rssrc/lib.rs,那么他就有两个crate,如果想有多个二进制craterust约定需要将文件放在src/bin目录下,每个文件就是一个单独的crate
  • crate根用来描述如何构建crate的文件。比如src/main.rs或者src/lib.rs就是crate根crate根文件将由Cargo传递给rustc来实际构建库或者二进制项目。
  • 带有Cargo.toml文件的包用来描述如何构建crate,一个包可以最多有一个库crate,任意多个二进制crate

github 代码地址

模块

模块以mod开头,下面创建了一个say模块

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

需要注意的是模块内,所有的项(函数、方法、结构体、枚举、模块和常量)默认都是私有的,可以用pub将项变为公有,上面的代码里pub fn hello()就是把函数hello()变为公有的。

子模块可以通过super访问父模块中所有的代码,包括私有代码。但是父模块中的代码不能访问子模块中的私有代码。

mod say {
pub fn hello() {
println!("Hello, world!");
}
fn hello_2() {
println!("hello")
}
pub mod hi {
pub fn hi_1() {
super::hello_2();
}
pub fn hi_2() {
println!("hi there");
}
}
}

同一文件内的模块

同一文件内的模块,最外层的mod say不用设置为pub就可以访问,但是mod say下面的要设置成pub才可以访问。

fn main() {
// 相对路径
say::hello();
// 绝对路径调用
crate::say::hello(); say::hi::hi_1();
say::hi::hi_2();
} mod say {
pub fn hello() {
println!("Hello, world!");
}
fn hello_2() {
println!("hello")
}
pub mod hi {
pub fn hi_1() {
super::hello_2();
}
pub fn hi_2() {
println!("hi there");
}
}
}

调用模块内的方法,可以使用绝对路径以crate开头,也就是从crate根开始查找,say模块定义在crate根 src/main.rs中,所以就可以这么调用crate::say::hello();绝对路径类似于Shell中使用/从文件系统根开始查找文件。

相对路径以模块名开始say,他定义于main()函数相同的模块中,类似Shell在当前目录开始查找指定文件say/hello

mod hi是一个嵌套模块,使用时要写比较长say::hi::hi_2();,可以使用use将名称引入作用域。

use crate::say::hi;

fn main() {
// 相对路径
say::hello();
// 绝对路径调用
crate::say::hello(); // 不使用 use
say::hi::hi_1();
say::hi::hi_2();
// 使用 use 后就可以这么调用
hi::hi_1();
}

使用pub use 重导出名称

不同的模块之前使用use引入,默认也是私有的。如果希望调用的模块内use引用的模块,就要用pub公开,也叫重导出

fn main() {
// 重导出名称
people::hi::hi_1();
people::hello();
// 但是不能
// people::say::hello();
} mod say {
pub fn hello() {
println!("Hello, world!");
}
fn hello_2() {
println!("hello")
}
pub mod hi {
pub fn hi_1() {
super::hello_2();
}
pub fn hi_2() {
println!("hi there");
}
}
} mod people {
// 重导出名称
pub use crate::say::hi;
use crate::say;
pub fn hello() {
say::hello();
}
}

如果想都导出自己和嵌入的指定包可以用self,例mod people_2 把模块people和嵌套模块info全部导出来了。

use crate::say::hi;

fn main() {
// 相对路径
say::hello();
// 绝对路径调用
crate::say::hello(); // 不使用 use
say::hi::hi_1();
say::hi::hi_2();
// 使用 use 后就可以这么调用
hi::hi_1(); // 重导出名称
people::hi::hi_1();
people::hello();
// 但是不能
// people::say::hello(); people_2::people::hello();
people_2::info::name();
} mod say {
pub fn hello() {
println!("Hello, world!");
}
fn hello_2() {
println!("hello")
}
pub mod hi {
pub fn hi_1() {
super::hello_2();
}
pub fn hi_2() {
println!("hi there");
}
}
} pub mod people {
// 重导出名称
pub use crate::say::hi;
use crate::say;
pub fn hello() {
say::hello();
}
pub mod info {
pub fn name() {
println!("zhangsang");
}
}
} mod people_2 {
// 重导出名称
pub use crate::people::{self, info};
pub fn hello() {
info::name();
}
}

不同文件夹的引用

方式一

看一下目录结构:



rust的约定,在目录下使用mod.rs将模块导出。

看一下user.rs的代码:

#[derive(Debug)]
pub struct User {
name: String,
age: i32
} impl User {
pub fn new_user(name: String, age: i32) -> User {
User{
name,
age
}
}
pub fn name(&self) -> &str {
&self.name
}
} pub fn add(x: i32, y: i32) -> i32 {
x + y
}

然后在mod.rs里导出:

pub mod user;

main.rs调用

mod user_info;
use user_info::user::User; fn main() {
let u1 = User::new_user(String::from("tom"), 5);
println!("user name: {}", u1.name());
println!("1+2: {}", user_info::user::add(1, 2));
}

方式二

看一下目录结构



和上面的不同之前是。这种方式是user_info目录里没有mod.rs,但是在外面有一个user_info.rs

user_info.rs中使用pub mod user;是告诉Rust在另一个与模块同名的文件夹内(user_info文件夹)内加载模块user。这也是rust的一个约定,但比较推荐用上面的方式。

代码和上面是一样的。

user.rs

#[derive(Debug)]
pub struct User {
name: String,
age: i32
} impl User {
pub fn new_user(name: String, age: i32) -> User {
User{
name,
age
}
}
pub fn name(&self) -> &str {
&self.name
}
} pub fn add(x: i32, y: i32) -> i32 {
x + y
}

user_info.rs里导出

pub mod user;

main.rs调用

mod user_info;
use user_info::user::User; fn main() {
let u1 = User::new_user(String::from("tom"), 5);
println!("user name: {}", u1.name());
println!("1+2: {}", user_info::user::add(1, 2));
}

使用外部包

使用外部包,一般就是从crates.io下载,当然也可以自己指写下载地点,或者使用我们本地的库,或者自建的的仓库。

一般方式

Cargo.tomldependencies下写要导入的依赖库

[dependencies]
regex = "0.1.41"

运行cargo build会从crates.io下载依赖库。

使用的时候,直接使用use引入

use regex::Regex;

fn main() {
let re = Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
println!("Did our date match? {}", re.is_match("2014-01-01"));
}

指定库地址

除了crates.io下载依赖库,也可以自己指定地址,也可以指定branch tag commit,比如下面这个

[dependencies]
# 可以和包不同名,也可以同名
my_rust_lib_1={package="my_lib_1",git="ssh://git@github.com/lpxxn/my_rust_lib_1.git",tag="v0.0.2"}

就是从github.com/lpxxn/my_rust_lib_1上下载包。也可以使用https

my_rust_lib_1={package="my_lib_1",git="https://github.com/lpxxn/my_rust_lib_1.git",branch="master"}

执行cargo build就会自动下载,使用的时候也是一样的。

use my_rust_lib_1;
fn main() {
println!("Hello, world!");
println!("{}", my_rust_lib_1::add(1, 2));
let u = my_rust_lib_1::User::new_user(String::from("tom"), 2);
println!("user: {:#?}", u);
}

使用本地的库

我们新建一个二进制库项目

cargo new pkg_demo_3

然后在pkg_demo_3内建一个库项目

cargo new --lib utils

然后就可以在 utils里写我们的库代码了

看一下现在的目录结构



utils库的user.rs里还是我们上面的代码

#[derive(Debug)]
pub struct User {
name: String,
age: i32
} impl User {
pub fn new_user(name: String, age: i32) -> User {
User{
name,
age
}
}
pub fn name(&self) -> &str {
&self.name
}
} pub fn add(x: i32, y: i32) -> i32 {
x + y
}

lib.rs里对user模块导出

pub mod user;
pub use user::User;

然后在我们的二进制库的Cargo.toml引入库

[dependencies]
utils = { path = "utils", version = "0.1.0" }

path就是库项目的路径

main.rs使用use引入就可以使用了

use utils::User;

fn main() {
let u = User::new_user(String::from("tom"), 5);
println!("user: {:#?}", u);
}

自建私有库

除了crates.io也可以自建registrie。这个有时间再重新写一篇帖子单独说,可以先看一下官方文档。

官方文档:registrie

依赖官方文档

帖子 github 代码地址

rust 模块组织结构的更多相关文章

  1. 结合源码看nginx-1.4.0之nginx模块组织结构详解

    目录 0. 摘要 1. nginx模块组织结构 2. nginx模块数据结构 3. nginx模块初始化 4. 一个简单的http模块 5. 小结 6. 参考资料 0. 摘要 nginx有五大优点:模 ...

  2. 【MM系列】SAP MM模块-组织结构介绍

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP MM模块-组织结构介绍   ...

  3. 【MM系列】SAP MM模块-组织结构第二篇

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP MM模块-组织结构第二篇   ...

  4. 【译】关于Rust模块的清晰解释

    原文链接: http://www.sheshbabu.com/posts/rust-module-system/ 原文标题: Clear explanation of Rust's module sy ...

  5. 2-6 Opencv模块组织结构

    https://opencv.org/releases.html https://sourceforge.net/projects/opencvlibrary/files/opencv-win/3.4 ...

  6. nginx 的模块及处理流程

         nginx的内部结构是由核心部分和一系列的功能模块所组成.这样划分是为了使得每个模块的功能相对简单,便于开发,同时也便于对系统进行功能扩展.这样的模块化设计类似于面向对象中的接口类,它增强了 ...

  7. Rust 1.31正式发布,首次引入Rust 2018新功能

    Rust 1.31是第一个实现了Rust 2018独有新功能并且不保证与现有代码库兼容的版本.Rust 2018相关工作正在进行中,而Rust 1.31只是整个三年开发周期的开始,这个开发周期将对这门 ...

  8. Duilib源码分析(一)整体框架

    Duilib界面库是一款由杭州月牙儿网络技术有限公司开发的界面开源库,以viksoe项目下的UiLib库的基础上开发(此后也将对UiLib库进行源码分析):通过XML布局界面,将用户界面和处理逻辑彻底 ...

  9. 结合源码看nginx-1.4.0之nginx事件驱动机制详解

    目录 0. 摘要 1. nginx事件模块组织结构 2. nginx事件模块数据结构及类图 3. nginx事件模块运行机制 4. 练习:一个简单的事件驱动模块 5. 小结 6. 参考源码

随机推荐

  1. 6.3 省选模拟赛 Decompose 动态dp 树链剖分 set

    LINK:Decompose 看起来很难 实际上也很难 考验选手的dp 树链剖分 矩阵乘法的能力. 容易列出dp方程 暴力dp 期望得分28. 对于链的情况 容易发现dp方程可以转矩阵乘法 然后利用线 ...

  2. luogu 3158 [CQOI2011]放棋子

    时隔多日 我又来挑战这道dp. 几个月前给写自闭了.几个月后再来. 首先一个我们能列出来的状态 是以行为转移的 f[i]表示前i行...但是会发现此时列我们控制不了 且棋子的颜色,个数我们也要放到状态 ...

  3. 线性DP 学习笔记

    前言:线性DP是DP中最基础的.趁着这次复习认真学一下,打好基础. ------------------ 一·几点建议 1.明确状态的定义 比如:$f[i]$的意义是已经处理了前$i个元素,还是处理第 ...

  4. sqlzoo刷题 SELECT from Nobel Tutorial

    SELECT from Nobel Tutorial 1.Change the query shown so that it displays Nobel prizes for 1950. SELEC ...

  5. QComboBox设置下拉item大小

    1.首先给ComboBox设置view: ui->comboBox->setView(new QListView()); 注意:这一句需要放在在所在界面的构造函数里,否则不生效. 2.给c ...

  6. python基础语法和实战练习

    (一)Python基础学习 Num01:python的基本数据类型 ①字符串:可进行拼接和截取 ②数字:int,float,complex(复数) 涉及到格式转换:int(x)转换为整数,float( ...

  7. 使用 Python 为女神挑选口红

    口红对于女生来说永远不嫌多,而男生也搞不明白珊瑚红.番茄色.斩男色等等颜色有什么区别,不都是红色么?当送给女神的口红是她不适合的,那结果就是口红进入垃圾箱还算是轻的,重则拉黑处理.男生们也不用着急,我 ...

  8. webgl实现发光线框(glow wireframe)效果

    在之前这篇文章, WebGL 单通道wireframe渲染 我们介绍了webgl如何实现单通道wireframe的效果. 本篇文章就是在此技术原理基础之上,来实现发光的wireframe效果. 要实现 ...

  9. 2020 Multi-University Training Contest 1 部分题解

    目录 Cookies Distinct Sub-palindromes Fibonacci Sum Finding a MEX Leading Robots Math is Simple Minimu ...

  10. FTP服务器搭建及自动备份设置

    本次随笔内容主要是FTP服务器搭建. 其实去年十月服务器就搭建完了.当时写了个PPT保存了一下,准备以后写博客,结果时隔快一年我自己都快要看不懂我自己写的PPT了  ( = o = ) 不过还是尽量尝 ...