Rust从入门到精通之入门篇:10.包和模块
包和模块
在本章中,我们将学习 Rust 的包和模块系统,它们是组织和重用代码的重要工具。随着项目规模的增长,良好的代码组织变得越来越重要,Rust 提供了一套强大的机制来管理代码结构。
包和 Crate
Crate
Crate 是 Rust 中最高级别的代码组织单位。一个 crate 可以是一个二进制项目或一个库。
- 二进制 crate:可以编译为可执行文件,必须有一个
main函数 - 库 crate:提供功能给其他 crate 使用,没有
main函数
包
包(package)是一个或多个 crate 的集合,提供一组功能。一个包包含一个 Cargo.toml 文件,描述如何构建这些 crate。
包的规则:
- 一个包最多可以包含一个库 crate
- 可以包含任意数量的二进制 crate
- 必须至少包含一个 crate(库或二进制)
Cargo 基础
Cargo 是 Rust 的构建系统和包管理器,它处理许多任务:
- 构建代码
- 下载依赖库
- 构建依赖库
创建新包
# 创建二进制包
cargo new my_project# 创建库包
cargo new my_library --lib
包结构
my_project/
├── Cargo.toml # 包配置文件
├── src/ # 源代码目录
│ └── main.rs # 二进制 crate 的根文件
└── target/ # 编译输出目录
对于库包,src/main.rs 被替换为 src/lib.rs。
Cargo.toml
Cargo.toml 是包的配置文件,使用 TOML (Tom’s Obvious, Minimal Language) 格式:
[package]
name = "my_project"
version = "0.1.0"
edition = "2021"[dependencies]
rand = "0.8.5"
定义模块
模块是 Rust 中组织代码的方式,可以控制项(函数、结构体等)的私有性。
创建模块
使用 mod 关键字创建模块:
// 在 lib.rs 或 main.rs 中
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}fn seat_at_table() {}}mod serving {fn take_order() {}fn serve_order() {}fn take_payment() {}}
}
模块树
模块形成一个树状结构,根模块是 src/main.rs 或 src/lib.rs:
crate└── front_of_house├── hosting│ ├── add_to_waitlist│ └── seat_at_table└── serving├── take_order├── serve_order└── take_payment
路径
路径用于在模块树中找到项。路径可以是:
- 绝对路径:从 crate 根开始,使用 crate 名或字面值
crate - 相对路径:从当前模块开始,使用
self、super或当前模块的标识符
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();
}
使用 super
super 关键字用于访问父模块中的项:
mod back_of_house {fn fix_incorrect_order() {cook_order();super::serve_order();}fn cook_order() {}
}fn serve_order() {}
公有和私有
Rust 中的项默认是私有的。可以使用 pub 关键字使项公有:
pub fn:公有函数pub struct:公有结构体(字段仍然是私有的)pub enum:公有枚举(所有变体都是公有的)
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 enum Appetizer {Soup, // 公有变体Salad, // 公有变体}
}pub fn eat_at_restaurant() {let mut meal = back_of_house::Breakfast::summer("Rye");meal.toast = String::from("Wheat");// 错误:seasonal_fruit 是私有的// meal.seasonal_fruit = String::from("blueberries");let order1 = back_of_house::Appetizer::Soup;let order2 = back_of_house::Appetizer::Salad;
}
使用 use 关键字
use 关键字可以将路径引入作用域,避免使用长路径:
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
}use crate::front_of_house::hosting;pub fn eat_at_restaurant() {hosting::add_to_waitlist();
}
使用 as 关键字
as 关键字可以为引入的项提供新名称:
use std::fmt::Result;
use std::io::Result as IoResult;fn function1() -> Result {// --snip--Ok(())
}fn function2() -> IoResult<()> {// --snip--Ok(())
}
重新导出
使用 pub use 可以重新导出项,使其可以被外部代码访问:
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
}pub use crate::front_of_house::hosting;pub fn eat_at_restaurant() {hosting::add_to_waitlist();
}
使用嵌套路径
可以在一个 use 语句中引入多个项:
// 而不是:
// use std::io;
// use std::io::Write;// 可以写成:
use std::io::{self, Write};
通配符
* 通配符可以引入路径下的所有公有项:
use std::collections::*;
这通常只在测试中使用,或者作为 prelude 模式的一部分。
将模块拆分为多个文件
随着模块增长,可能需要将它们移动到单独的文件中。
文件结构
src/
├── front_of_house/
│ ├── hosting.rs
│ └── serving.rs
├── front_of_house.rs
└── lib.rs
代码组织
// src/lib.rs
mod front_of_house;pub use crate::front_of_house::hosting;pub fn eat_at_restaurant() {hosting::add_to_waitlist();
}
// src/front_of_house.rs
pub mod hosting;
mod serving;
// src/front_of_house/hosting.rs
pub fn add_to_waitlist() {}
fn seat_at_table() {}
// src/front_of_house/serving.rs
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
工作区(Workspaces)
工作区是一组共享 Cargo.lock 和输出目录的包。
创建工作区
创建一个包含 Cargo.toml 的目录:
# Cargo.toml
[workspace]
members = ["adder","add_one",
]
然后创建成员包:
cargo new adder
cargo new add_one --lib
工作区结构
add/
├── Cargo.lock
├── Cargo.toml
├── adder/
│ ├── Cargo.toml
│ └── src/
│ └── main.rs
└── add_one/├── Cargo.toml└── src/└── lib.rs
依赖关系
工作区中的包可以相互依赖:
# adder/Cargo.toml
[dependencies]
add_one = { path = "../add_one" }
发布 crate 到 crates.io
准备发布
- 添加文档注释
- 设置元数据:
[package]
name = "my_crate"
version = "0.1.0"
edition = "2021"
description = "A description of my crate"
license = "MIT OR Apache-2.0"
发布命令
cargo publish
版本管理
Cargo 遵循语义化版本规范(SemVer):
- 主版本号:不兼容的 API 更改
- 次版本号:向后兼容的功能添加
- 修订号:向后兼容的错误修复
示例程序
让我们创建一个简单的库,展示 Rust 的模块系统:
// src/lib.rs
pub mod math;
pub mod utils;pub fn greet() {println!("Hello from my_library!");utils::logging::log("Greeted the user");
}
// src/math.rs
pub mod arithmetic;
pub mod statistics;pub fn is_even(n: i32) -> bool {n % 2 == 0
}
// src/math/arithmetic.rs
pub fn add(a: i32, b: i32) -> i32 {a + b
}pub fn subtract(a: i32, b: i32) -> i32 {a - b
}pub fn multiply(a: i32, b: i32) -> i32 {a * b
}pub fn divide(a: i32, b: i32) -> Option<i32> {if b == 0 {None} else {Some(a / b)}
}
// src/math/statistics.rs
pub fn mean(numbers: &[i32]) -> Option<f64> {if numbers.is_empty() {return None;}let sum: i32 = numbers.iter().sum();Some(sum as f64 / numbers.len() as f64)
}pub fn median(numbers: &[i32]) -> Option<f64> {if numbers.is_empty() {return None;}let mut sorted = numbers.to_vec();sorted.sort();let mid = sorted.len() / 2;if sorted.len() % 2 == 0 {Some((sorted[mid - 1] as f64 + sorted[mid] as f64) / 2.0)} else {Some(sorted[mid] as f64)}
}
// src/utils.rs
pub mod logging;
// src/utils/logging.rs
pub fn log(message: &str) {println!("[LOG]: {}", message);
}pub fn error(message: &str) {eprintln!("[ERROR]: {}", message);
}
使用这个库的二进制 crate:
// src/main.rs
use my_library::math::arithmetic;
use my_library::math::statistics;
use my_library::utils::logging;fn main() {my_library::greet();let a = 10;let b = 5;println!("{}+{}={}", a, b, arithmetic::add(a, b));println!("{}-{}={}", a, b, arithmetic::subtract(a, b));println!("{}*{}={}", a, b, arithmetic::multiply(a, b));match arithmetic::divide(a, b) {Some(result) => println!("{}/{}={}", a, b, result),None => logging::error("除以零错误"),}let numbers = vec![1, 5, 10, 15, 20];println!("数字: {:?}", numbers);if let Some(mean_value) = statistics::mean(&numbers) {println!("平均值: {}", mean_value);}if let Some(median_value) = statistics::median(&numbers) {println!("中位数: {}", median_value);}logging::log("程序执行完毕");
}
练习题
-
创建一个名为
geometry的库 crate,包含计算不同形状(圆形、矩形、三角形)面积和周长的函数。使用适当的模块结构组织代码。 -
扩展上面的库,添加一个
utils模块,包含用于验证输入值的函数(例如,检查边长是否为正数)。 -
创建一个二进制 crate,使用你的
geometry库计算并打印不同形状的面积和周长。 -
创建一个工作区,包含两个相关的包:一个提供基本数学运算,另一个使用这些运算实现更复杂的功能。
-
修改
geometry库,使其可以发布到 crates.io(添加适当的文档、描述和许可证信息)。
总结
在本章中,我们学习了:
- Rust 的包和 crate 系统
- 使用 Cargo 管理项目
- 创建和组织模块
- 控制项的可见性(公有和私有)
- 使用路径引用模块中的项
- 使用
use关键字简化路径 - 将模块拆分到多个文件
- 创建和管理工作区
- 准备和发布 crate
良好的代码组织是构建可维护软件的关键。Rust 的模块系统提供了强大的工具,帮助你组织代码,控制可见性,并创建清晰的 API。随着你的 Rust 项目变得更加复杂,这些技能将变得越来越重要。
相关文章:
Rust从入门到精通之入门篇:10.包和模块
包和模块 在本章中,我们将学习 Rust 的包和模块系统,它们是组织和重用代码的重要工具。随着项目规模的增长,良好的代码组织变得越来越重要,Rust 提供了一套强大的机制来管理代码结构。 包和 Crate Crate Crate 是 Rust 中最高…...
ChatDBA VS DeepSeek:快速诊断 OceanBase 集群新租户数据同步异常
社区王牌专栏《一问一实验:AI 版》改版以来已发布多期(51-60),展现了 ChatDBA 在多种场景下解决问题的效果。 下面让我们正式进入《一问一实验:AI 版》第 62 期,看看 ChatDBA 最新效果以及与热门大模型 De…...
dify忘记密码
特别好,非常好,一把年纪忘了dify的账号、密码了,very good!!! 参考如下教程 https://zhuanlan.zhihu.com/p/24515387167 rootbae577d82ec7:/# psql -U postgres psql: error: connection to server on so…...
Python----计算机视觉处理(Opencv:图像边缘检测:非极大值抑制,双阈值筛选)
一、 高斯滤波 边缘检测本身属于锐化操作,对噪点比较敏感,所以需要进行平滑处理。这里使用的是一个5*5的高斯 核对图像进行消除噪声。 二、计算图像的梯度和方向 三、非极大值抑制 在得到每个边缘的方向之后,其实把它们连起来边缘检测就算完了…...
vue3(笔记)5.0--pinia工具的知识扩展
pinia工具 defineStore(创建pinia) 作用:用于定义一个 Pinia store。 用法: 接收一个唯一的 ID 和一个配置对象,配置对象中可以定义 state、getters 和 actions。state 是一个函数,返回初始状态。getters 类似于 Vue 组件中的计…...
基于Kubernetes部署Prometheus监控平台
#作者:stackofumbrella 文章目录 prometheus和k8s集群版本对照表架构Prometheus Operator简介kube-prometheus下载地址 安装修改镜像地址修改Prometheus的service修改Grafana的service修改Alertmanager的service数据持久化执行安装 Prometheus验证Grafana验证解决C…...
往期项目shader着色器实践效果应用合集
1、管路混色 2、水管水流效果 3、水管流入到流完效果 4、加热冷却 两 色混色 示意 XX、毒蘑菇测试效果...
如何在 React 项目中使用React.lazy和Suspense实现组件的懒加载?
大白话如何在 React 项目中使用React.lazy和Suspense实现组件的懒加载? 在 React 项目里,有时候组件功能多、体积大,要是一次性把所有组件都加载进来,网页加载速度就会变慢。而 React 提供了 React.lazy 和 Suspense 这两个好东西…...
绿色暴政:Relax Max如何用军工科技定义环保新标准
《绿色暴政:Relax Max如何用军工科技定义环保新标准》 ——从隐形战斗机涂层到零碳卫浴的降维打击 (洛克希德马丁实验室,2023年)当F-35战斗机的隐形涂料配方被改写为卫浴釉料时,环保产业迎来了最硬核的颠覆者。Relax…...
蓝桥杯刷题 Day 4 栈与链表
蓝桥杯刷题 Day 4 栈与链表 文章目录 蓝桥杯刷题 Day 4 栈与链表前言一、栈1. 解题思路2. 拆解代码(不复杂,不拆了) 二、链表1. 解题思路1.1 主函数1.2 自定义列表类1.2.1 插入操作1.2.2 删除操作1.2.3 按要求输出 三、 题后收获3.1 知识点 前…...
第十三届蓝桥杯单片机省赛程序设计试题
目录 试题 各程序块代码 init.c main.c other.h other.c key.c seg.c onewire.c部分 ds1302.c部分 试题 各程序块代码 init.c #include "other.h"void init74hc138(unsigned char n){P2(P2&0x1f)|(n<<5);P2&0x1f; } void init(){P00x00;in…...
QOpenGLWidget动态加载功能实现教程(Qt+OpenGL)
QOpenGLWidget动态加载功能实现教程 我需要在Qt里面使用QOpenGLWidget显示OpenGL窗口,并且需要实现加载模型后重新渲染更新窗口的功能,但是一直无法更新被卡住了,现在把问题解决了总结一下整个实现过程。 创建一个自己的OpenGLWidget类 QOp…...
机器学习正则化技术:Ridge、Lasso与ElasticNet全解析
机器学习中的正则化技术 在机器学习中,正则化技术(如 Ridge 和 Lasso)主要用于解决过拟合问题,通过限制模型复杂度提高泛化能力。以下是详细说明及实例代码: 一、正则化解决的问题 过拟合:模型在训练集表…...
数字转换(c++)
【题目描述】 如果一个数 xx 的约数和 yy (不包括他本身)比他本身小,那么 xx 可以变成 yy ,yy 也可以变成 xx 。例如 44 可以变为 33 ,11 可以变为 77 。限定所有数字变换在不超过 nn 的正整数范围内进行,…...
ESP32驱动BMP280和MQ4传感器
文章目录 前言 一、硬件准备 所需组件 连接方式: 二、软件实现 1.所需库 2.代码实现 效果演示 三、上传Qt端 前言 在物联网和环境监测应用中,传感器是获取环境数据的关键组件。本文将详细介绍如何使用ESP32微控制器同时驱动BMP280大气压力传感器…...
洛谷题单1-B2002 Hello,World!-python-流程图重构
题目描述 编写一个能够输出 Hello,World! 的程序。 提示: 使用英文标点符号;Hello,World! 逗号后面没有空格。H 和 W 为大写字母。 输入格式 无 输出格式 无 输入输出样例 #1 输入 #1 无输出 #1 Hello,World!方式-print() 代码 class Solut…...
MQTT协议笔记
消息格式 MQTT(Message Queuing Telemetry Transport)是一种轻量级的消息协议,专为低带宽、高延迟或不可靠的网络设计,广泛应用于物联网(IoT)设备之间的通信。MQTT消息体的结构遵循MQTT协议规范࿰…...
CentOS系统下安装tesseract-ocr5.x版本
CentOS系统下安装tesseract-ocr5.x版本 安装依赖包: yum update -y yum install autoconf automake libtool libjpeg-devel libpng-devel libtiff-devel zlib-devel yum install automake libtool bzip2 -y手动编译安装GCC(因系统默认安装的GCC版本比较…...
“征服HTML引号恶魔:“完全解析手册”!!!(quot;表示双引号)
🚨📢 "征服HTML引号恶魔:“完全解析手册” 📢🚨 🎯 博客引言:当引号变成"恶魔" 😱 是否遇到过这种情况: 写HTML时满心欢喜输入<div title"他…...
如何使用VS中的Android Game Development Extension (AGDE) 来查看安卓 Logcat 日志
一、首先按照以下 指引 中的 第1、2步骤,安装一下 AGDE ,AGDE 的安装包可以在官网上找到。 UE4 使用AndroidGameDevelopmentExtension(AGDE)对安卓客户端做“断点调试”与“代码热更”-CSDN博客 在执行第二步骤前,记得…...
VSCode 生成HTML 基本骨架
在VSCode 新建html文件中敲一个英文感叹号 ! <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><titl…...
【Spring AI】基于专属知识库的RAG智能问答小程序开发——功能优化:用户鉴权相关工具类代码
系列文章目录 【Spring AI】基于专属知识库的RAG智能问答小程序开发——完整项目(含完整前端后端代码)【Spring AI】基于专属知识库的RAG智能问答小程序开发——代码逐行精讲:核心ChatClient对象相关构造函数【Spring AI】基于专属知识库的R…...
Solr-搜索引擎-入门到精通
以下是对 Apache Solr 的简介及其常用语法的快速入门指南: 一、Solr 是什么? • 核心定位:Apache Solr 是一个基于 Lucene 的高性能、开源的搜索平台,支持全文检索、分词、高亮、聚合统计等功能。 • 核心功能: • 全…...
07_GRU模型
GRU模型 双向GRU笔记:https://blog.csdn.net/weixin_44579176/article/details/146459952 概念 GRU(Gated Recurrent Unit)也称为门控循环单元,是一种改进版的RNN。与LSTM一样能够有效捕捉长序列之间的语义关联,通过引入两个&qu…...
【字符设备驱动开发–IMX6ULL】(二)Linux 设备号
【字符设备驱动开发–IMX6ULL】(二)Linux 设备号 文章目录 【字符设备驱动开发–IMX6ULL】(二)Linux 设备号1 设备号的组成2.设备号的分配 1 设备号的组成 为了方便管理,Linux 中每个设备都有一个设备号,设…...
【大模型基础_毛玉仁】3.4 Prompt 技巧
目录 3.4 Prompt 技巧3.4.1 规范Prompt 编写1)任务说明要明确2)上下文丰富且清晰3)输出格式要规范4)排版要清晰 3.4.2 合理归纳提问1)复杂问题拆解2)追问 3.4.3 适时使用CoT1)何时使用CoT2&…...
探索PyMOL新插件NRGSuite-Qt:全面提升分子对接、结合位点预测与动力学模拟的研究效率
随着分子建模和计算生物学的快速发展,分子对接(Molecular Docking)、结合位点预测、相互作用分析以及动力学研究等领域的工具越来越重要。这些工具不仅帮助研究人员理解分子间的相互作用机制,还能加速药物设计和优化过程。NRGSuit…...
sql2022 复制 事务级别发布后无法删除
Cannot execute as the database principal because the principal "dbo" does not exist, this type of principal cannot be impersonated, or you do not have permission. 用SA用户登录执行下列语句 USE [xxxxx] GO EXEC dbo.sp_changedbowner loginame Nsa, …...
wokwi arduino mega 2560 - 键盘与LCD显示
截图: 链接: https://wokwi.com/projects/414520193913760769 代码: //cslg lcd key #include <LiquidCrystal.h> // 引入LiquidCrystal库,用于LCD显示 #include <Keypad.h> // 引入Keypad库,用于键盘输…...
Linux设置SSH免密码密钥登录
文章目录 设置SSH免密码密钥登录第一步: 生成SSH密钥对(在客户端操作)方式一:Windows 10/11 内置的 OpenSSH 客户端(推荐)常用选项:密钥算法选择建议生成秘钥 方式二:借用Xshell工具…...
