我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Rust如何进行模块化开发技巧分享

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Rust如何进行模块化开发技巧分享

类似es6的模块化,Rust通过package、create、module来实现代码的模块化管理

Rust如何进行模块化开发?

Rust的代码组织包括:哪些细节可以暴露,哪些细节是私有的,作用域内哪些名称有效等等。

  • Package(包):Cargo的特性,让你构建、测试、共享create
  • Create(单元包):一个模块树,它可以产生一个library或可执行文件
  • Module(模块)、use:让你控制代码的组织、作用域、私有路径
  • Path(路径):为struct、function或module等项命名的方式

Package和Create

create的类型:

  • binary(二进制create)
  • library(库create)

其中,关于Create,还有个概念——Create Root:

是源代码文件Rust编译器从这里开始,组成你的Create的根Module

一个Package:

  • 包含一个Cargo.toml,它描述了如何构建这些Crates
  • 只能包含0-1个library create(库create)
  • 可以包含任意数量的binary create(二进制create)
  • 但必须至少包含一个create(library或binary)

我们使用cargo新建一个项目

然后会提示: Created binary (application) my-project package,这代表我们创建了一个二进制的应用程序,名叫my-project的package

我们进入这个文件夹:

我们可以看到class="lazy" data-src/min.rs文件,这是我们程序的入口文件,但是我们在Cargo.toml中并没有看到相关的配置:

[package]
name = "my-project"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies] 

这是因为cargo有一些惯例

Cargo的惯例

  • class="lazy" data-src/main.rs是binary create的create root* create的名与package名相同如果我们还有一个这个文件:class="lazy" data-src/lib.rs,那么:
  • 表明package包含一个library create
  • 它是library create的create root
  • create的名与package名相同

Cargo将会把create root文件交给rustc(rust编译器)来构建library或者binary

一个Package可以同时包含class="lazy" data-src/main.rs和class="lazy" data-src/lib.rs

一个Package也可以有多个binary create:

  • 文件放在class="lazy" data-src/bin,放在这里的每个文件都是单独的binary create

Create的作用

将相关功能组合到一个作用域内,便于在项目间进行共享。

同时,这也能防止命名冲突,例如rand create,访问它的功能需要通过它的名字:rand

定义module来控制作用域和私有性

Module:

  • 在一个create内,将代码进行分组
  • 增加可读性,易于复用
  • 控制项目(item)的私有性。public,private

建立module:

  • mod关键字
  • 可嵌套
  • 可包含其他项(struct、enum、常量、trait、函数等)的定义
mod front_of_house {mod hosting {fn add_to_waitlist() {}fn seat_at_table() {}}mod serving {fn take_order() {}fn serve_order() {}fn take_payment() {}}
} 

class="lazy" data-src/main.rs 和 class="lazy" data-src/lib.rs 叫做create roots:

  • 这两个文件(任意一个)的内容形成了名为create的模块,位于整个模块树的根部
  • 整个模块树在隐式的模块下

路径Path

路径的作用是为了在rust的模块中找到某个条目

路径的两种形式:

  • 绝对路径:从create root开始,使用create名或字面值create
  • 相对路径:从当前模块开始,使用self(本身),super(上一级)或当前模块的标识符

路径至少由一个标识符组成,标识符之间使用::

举个例子(下面这段程序将报错,我们将在后面讲到如何解决):

mod front_of_house {mod hosting {fn add_to_waitlist() {}}
}

pub fn eat_at_restaurant() {crate::front_of_house::hosting::add_to_waitlist();//绝对路径front_of_house::hosting::add_to_waitlist();//相对路径
} 

那么为什么会报错呢?

我们查看报错的原因:module hosting is private,编译器告诉我们,hosting这个module是私有的。至此,为了解决这个问题,我们应该去了解一下私有边界

私有边界(private boundary)

  • 模块不仅可以组织代码,还可以定义私有边界
  • 如果把函数或struct等设为私有,可以将它放到某个模块中。
  • rust中所有的条目(函数,方法,struct,enum,模块,常量)默认情况下是私有的
  • 父级模块无法访问子模块中的私有条目
  • 但是在子模块中可以使用所有祖先模块中的条目

为什么rust默认这些条目是私有的呢?因为rust希望能够隐藏内部的实现细节,这样就会让开发者明确知道:更改哪些内部代码的时候,不会破坏外部的代码。同时,我们可以使用pub关键字将其声明为公共的。

pub关键字

rust默认这些条目为私有的,我们可以使用pub关键字来将某些条目标记为公共的。

我们将hosting声明pub,add_to_waitlist这个function也要声明pub

mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
}

pub fn eat_at_restaurant() {crate::front_of_house::hosting::add_to_waitlist();//绝对路径front_of_house::hosting::add_to_waitlist();//相对路径
} 

为什么front_of_house这个mod不需要添加pub呢?因为它们是同级的。

super关键字

super:用来访问父级模块路径中的内容,类似文件系统中的..

fn serve_order() {}
mod front_of_house {fn fix_incorrect_order() {cook_order();super::serve_order();}fn cook_order() {}
} 

pub struct

声明一个公共的struct就是将pub放在struct前:

mod back_of_house {pub struct Breakfast {}
} 

声明了一个公共的struct后:

  • struct是公共的
  • struct的字段默认是私有的

而我们想让struct中的字段为公有的必须在前面加上pub

mod back_of_house {pub struct Breakfast {pub toast: String,//公有的seasonal_fruit: String, //私有的}
} 

也就是说:struct的字段需要单独设置pub来变成公有

我们看一个例子:

mod back_of_house {pub struct Breakfast {pub toast: String,//公有的seasonal_fruit: String, //私有的}impl Breakfast {//一个关联函数pub fn summer(toast: &str) -> Breakfast {Breakfast {toast: String::from(toast),seasonal_fruit: String::from("peaches"),}}}
}

pub fn eat_at_restaurant() {let mut meal = back_of_house::Breakfast::summer("Rye");meal.toast = String::from("Wheat");println!("I'd like {} toast please", meal.toast);meal.seasonal_fruit = String::from("blueberries");//报错:field `seasonal_fruit` is private
} 

pub enum

声明一个公共的enum就是将pub放在enum前:

mod back_of_house {pub enum Appetizer {}
} 

我们声明了一个公共的enum后:

  • enum是公共的
  • enum的变体也都是公共的
mod back_of_house {pub enum Appetizer {Soup,//公共的Salad, //公共的}
} 

为什么呢?因为枚举里面只有变体,只有变体是公共的这个枚举才有用。而struct中某些部分为私有的也不影响struct的使用,所以rust规定公共的struct中的字段默认为私有的。

Use关键字

我们可以使用use关键字将路径导入到作用域内,而我们引入的东西也任然遵循私有性规则(公共的引入的才能用)

mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}fn some_function() {}//私有的,使用use导入后,外部依然不能调用这个函数}
}

use crate::front_of_house::hosting;
// 相当于mod hosting {}

pub fn eat_at_restaurant() {hosting::add_to_waitlist();hosting::add_to_waitlist();hosting::add_to_waitlist();
} 

使用use来指定相对路径(和使用条目时的规则相同):

use front_of_house::hosting; 

我们可以注意到我们调用的add_to_waitlist是导入的hostingmod下的,那我们可不可以直接导入function呢?

当然是可以的(不过并不推荐直接导入方法):

mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
}

use crate::front_of_house::hosting::add_to_waitlist;
// 相对于mod hosting {}

pub fn eat_at_restaurant() {add_to_waitlist();
} 

use的习惯用法

当我们直接导入方法时,我们有可能就搞不清楚是从其他模块导入的还是在这个作用域下声明的。

所以,通常情况下,我们导入的通常为父级模块。

//...
use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {hosting::add_to_waitlist();
} 

不过,struct,enum,其他:指定完整路径(指定到本身)

use std::collections::HashMap;
fn main() {let mut map = HashMap::new();map.insert(1, 2);
} 

但是同名的条目,我们在引入时需指定父级模块(比如下面的例子,两个类型都叫Result)

use std::fmt;
use std::io;

fn f1() -> fmt::Result {//...
}

fn f2() -> io::Result {//...
}
//... 

as关键字

关于上面同名的问题,还有另一种解决方法:使用as关键字

as关键字可以为引入的路径指定本地的别名

use std::fmt::Result;
use std::io::Result as IoResult;

fn f1() -> Result {//...
}

fn f2() -> IoResult {//...
} 

使用 pub use 重新导出名称

使用 use 将路径(名称)导入到作用域内后,该名称在此作用域内是私有的,外部的模块是没办法访问use导入的模块的。

由前面pub的作用可知,类似pub fn、pub mod,我们可以使用pub use来导入,相当于它导入了这个内容,然后又将它导出了。

(当我们使用pub use时会发现没有警告:“导入了但没有使用”,因为它同时也导出了,也被视作使用了这个导入的内容)

导入外部包

我们通过在Cargo.toml中的[dependencies]添加依赖:

# ...
[dependencies]
rand = "^0.8.5" 

出现:Blocking waiting for file lock on package cache

删除User/.cargo文件夹中的.package-cache文件。重新执行cargo build下载依赖。

很多时候我们的下载速度很慢,我们可以将下载源换到国内,在用户文件夹下的.cargo文件夹中添加 config 文件,写入以下内容:

[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
# 如果所处的环境中不允许使用 git 协议,可以把上面的地址改为
# registry = "https://mirrors.ustc.edu.cn/crates.io-index"
#[http]
#check-revoke = false 

这时候cargo build就会很快了。

我们这样导入:

use rand::Rng; 

另外:标准库也被当做外部包,需要导入,并且:

  • 我们不需要修改Cargo.toml来添加依赖
  • 需要使用use将std的特定条目导入到当前作用域 use多次导入(嵌套导入)
use std::{ascii, io};
//相当于:use std::ascii;
// use std::io; 

这样的导入该如何简写呢?

use std::io;
use std::io::Chain; 

可以使用self

use std::io::{self, Chain}; 

如何将模块放入其他文件?

假如我们的class="lazy" data-src/lib.rs中的内容是这样:

mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
}
//... 

mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
}
//... 

我们可以在lib.rs同级目录下新建front_of_house.rs,然后将模块内容写在文件中:

front_of_house.rs

pub mod hosting {pub fn add_to_waitlist() {}
} 

lib.rs

mod front_of_house;
//... 

如果我们想将hosting模块的内容单独存放呢?

我们需要新建一个front_of_house文件夹,并新建hosting.rs文件

hosting.rs

pub fn add_to_waitlist() {} 

front_of_house.rs

pub mod hosting; 

lib.rs

mod front_of_house;//... 

原来的文件内容:

mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
} 

随着模块逐渐变大,这项功能将能够帮助我们更好的管理代码

到此这篇关于Rust如何进行模块化开发的文章就介绍到这了,更多相关Rust模块化开发内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Rust如何进行模块化开发技巧分享

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Rust如何进行模块化开发技巧分享

Rust模块化,模块化有助于代码的管理和层次逻辑的清晰,本文主要介绍了Rust如何进行模块化开发,结合实例代码给大家讲解的非常详细,需要的朋友可以参考下
2023-01-15

Python开发经验分享:如何进行代码重用和模块化设计

Python开发经验分享:如何进行代码重用和模块化设计引言:在软件开发中,代码的重用和模块化设计是非常重要的。它们能够提高代码的可维护性、可读性和可测试性,进而提高开发效率。Python作为一种高级编程语言,具备了强大的代码重用和模块化设计
Python开发经验分享:如何进行代码重用和模块化设计
2023-11-22

C++开发建议:如何进行模块化的C++开发

C++语言作为一种通用的高级编程语言,被广泛用于开发各种应用程序和系统。然而,C++的复杂性和灵活性也使得开发人员面临着一些挑战,特别是在大型项目中。在处理大型项目时,模块化的开发方法是至关重要的。本文将介绍如何进行模块化的C++开发,并提
C++开发建议:如何进行模块化的C++开发
2023-11-23

Golang开发经验分享:如何进行性能优化

Golang开发经验分享:如何进行性能优化摘要:随着互联网的快速发展,对于应用程序的性能要求越来越高。作为一门高效且易于编程的语言,Golang 在开发高性能应用方面表现出色。本文将从多个方面介绍如何进行 Golang 的性能优化,包括并发
Golang开发经验分享:如何进行性能优化
2023-11-22

JavaScript模块化进阶指南:揭秘专业开发者的必备技巧

JavaScript模块化是构建大型复杂应用程序的关键技术,通过将代码组织成独立模块,可以提高代码的可维护性和可复用性。本文将深入探讨JavaScript模块化,介绍如何使用模块化编写代码,以及如何使用ES6和Webpack等工具管理模块。
JavaScript模块化进阶指南:揭秘专业开发者的必备技巧
2024-02-26

Python开发经验分享:如何进行有效的性能优化

Python开发经验分享:如何进行有效的性能优化引言:随着Python在开发领域的广泛应用,越来越多的开发者开始面临性能优化的挑战。在许多情况下,提高代码的执行效率是实现更好用户体验、更高并发处理能力和更低成本的关键所在。本文将分享一些有效
Python开发经验分享:如何进行有效的性能优化
2023-11-22

Python开发经验分享:如何进行性能测试和优化

Python是一种简单易用、高效的编程语言,在众多开发领域中都有广泛应用。然而,随着项目的不断增长和复杂度的提高,性能问题常常成为开发者面临的挑战之一。本文将分享我在Python开发中的经验,重点介绍如何进行性能测试和优化,帮助开发者更好地
Python开发经验分享:如何进行性能测试和优化
2023-11-22

Python开发经验分享:如何进行代码重构和优化

Python开发经验分享:如何进行代码重构和优化引言:随着软件开发的不断发展,代码的重构和优化已成为开发过程中不可或缺的一环。而Python作为一门动态、简洁的高级编程语言,也同样需要进行代码重构和优化来提高程序的性能和可维护性。本文将分享
Python开发经验分享:如何进行代码重构和优化
2023-11-22

C++开发经验分享:如何进行跨平台C++开发

C++是一种功能强大的编程语言,它广泛应用于各种领域的软件开发中。然而,由于不同操作系统的差异,C++开发人员经常面临一个问题:如何进行跨平台C++开发?本文将分享一些C++开发经验,帮助您在跨平台开发中取得成功。了解目标平台特性首先,您需
C++开发经验分享:如何进行跨平台C++开发
2023-11-22

Golang开发经验分享:如何进行高效的测试驱动开发

Golang是一种非常流行的编程语言,其在Web开发、云计算和互联网领域具有广泛的应用。而测试驱动开发(Test Driven Development,简称TDD)则是一种开发方式,它可以帮助我们在开发过程中更高效、更精确地编写代码。那么,
Golang开发经验分享:如何进行高效的测试驱动开发
2023-11-22

Golang开发经验分享:如何进行高效的性能分析

随着计算机技术的不断发展,性能已成为了软件开发中重要的指标之一。而在Golang开发过程中,性能也是一个非常重要的方面。为了保证程序的高效率运行,必须进行性能分析和优化。下面就为大家分享一下如何进行高效的性能分析。1.基本概念在进行性能分析
Golang开发经验分享:如何进行高效的性能分析
2023-11-23

Python开发经验分享:如何进行版本控制和发布管理

Python开发经验分享:如何进行版本控制和发布管理引言:在Python开发过程中,版本控制和发布管理是非常重要的环节。通过版本控制,我们可以轻松地追踪代码的更改、协同开发、解决冲突等;而发布管理则能够帮助我们组织代码的部署、测试和发布过程
Python开发经验分享:如何进行版本控制和发布管理
2023-11-23

如何使用Python中的模块管理工具进行软件开发

如何使用Python中的模块管理工具进行软件开发在Python的世界里,有许多优秀的第三方模块可以帮助我们更高效地开发软件。然而,当项目规模逐渐增大时,引入的模块也会越来越多,这就需要一个良好的模块管理工具来管理这些模块之间的依赖关系。Py
2023-10-22

Golang开发经验分享:如何进行有效的单元测试

Golang是一种由Google开发的开源编程语言,它因其高效的并发性能和简洁的语法而备受开发者青睐。在Golang开发中,单元测试是保证代码质量和稳定性的重要手段。本文将分享如何进行有效的Golang单元测试,帮助开发者提高代码的可靠性和
Golang开发经验分享:如何进行有效的单元测试
2023-11-22

Golang开发经验分享:如何进行高效的代码复用

Golang作为一门现代化的编程语言,以其高效的性能和简洁的语法受到了广泛的关注和使用。在Golang开发过程中,代码复用是一个非常重要的概念。它可以提高代码的可维护性、减少重复开发和代码冗余。本文将分享一些关于如何进行高效的代码复用的经验
Golang开发经验分享:如何进行高效的代码复用
2023-11-22

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录