ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

rust 学习(11):包、Crate、模块系统

rust 学习(11):包、Crate、模块系统

文章目录

    • 一、回顾与导入
    • 二、核心概念
      • 什么是 Crate?
      • 什么是 Package?
    • 三、定义模块
      • 3.1 使用 `mod` 关键字
      • 3.2 模块的私有性
    • 四、路径(Path)
      • 使用 `super` 访问父模块
    • 五、use 关键字
      • 5.1 使用 `as` 重命名
      • 5.2 导入多项
      • 5.3 通配符 `*`
      • 5.4 嵌套路径
    • 六、拆分模块到多个文件
      • 6.1 传统方式(Rust 2015 风格,仍可用)
      • 6.2 现代方式(Rust 2018+,推荐)
      • 6.3 另一种现代方式(不推荐 mod.rs)
    • 七、pub use —— 重导出
    • 八、Cargo.toml 与第三方依赖
      • 8.1 添加依赖
      • 8.2 使用第三方 crate
    • 九、完整项目示例
      • 项目结构
      • Cargo.toml
      • src/lib.rs
      • src/front_of_house/mod.rs
      • src/front_of_house/hosting.rs
      • src/back_of_house.rs
      • src/main.rs
    • 十、常见陷阱与最佳实践
      • 陷阱 1:忘记添加 `pub`
      • 陷阱 2:路径混乱
      • 最佳实践总结
    • 十一、总结与速查
    • 十二、思考题
    • 参考链接

返回首页 | 上篇 | 下篇

掌握 Rust 的模块化组织 —— 包、Crate、模块、use 以及项目结构最佳实践


一、回顾与导入

前面十篇文章我们学习了 Rust 的基础语法和核心特性。随着项目规模增长,我们需要一种方式来组织代码,将功能拆分到不同的文件中,并控制哪些内容对外可见。

这就是 Rust 的模块系统的职责。

💡 Rust 的模块系统帮助你组织代码、控制私有性、管理依赖。


二、核心概念

Rust 的模块系统包含以下几个层级(从大到小):

概念说明示例
包(Package)一个或多个 Crate,有Cargo.tomlcargo new my_project
Crate一个编译单元,产生一个库或可执行文件lib.rsmain.rs
模块(Module)控制作用域和私有性的代码组织单元mod network
路径(Path)引用模块中的项crate::network::connect

什么是 Crate?

Crate 是 Rust 的编译单元。rustc每次编译,处理的都是一个 Crate。Crate 可以生成:

  • 二进制程序(有main函数)
  • 库(可被其他项目使用,没有main函数)

什么是 Package?

Package 包含:

  • 一个Cargo.toml文件
  • 至少一个 Crate(可以是库、二进制或两者都有)
典型结构: my_package/ ├── Cargo.toml ├── src/ │ ├── main.rs # 二进制 crate 根 │ └── lib.rs # 库 crate 根(可选)

三、定义模块

3.1 使用mod关键字

可以在一个文件中定义多个模块:

// src/main.rsmodfront_of_house{modhosting{fnadd_to_waitlist(){}fnseat_at_table(){}}modserving{fntake_order(){}fnserve_order(){}fntake_payment(){}}}

3.2 模块的私有性

Rust 中所有项(函数、结构体、枚举等)默认是私有的。父模块不能访问子模块的私有项,但子模块可以访问父模块的项。

modfront_of_house{pubmodhosting{// 添加 pub 让模块对外可见pubfnadd_to_waitlist(){}// 添加 pub 让函数对外可见}}fnmain(){// 需要 pub 才能访问crate::front_of_house::hosting::add_to_waitlist();}

四、路径(Path)

路径用于引用模块中的项,有两种形式:

类型语法说明
绝对路径crate::module::item从当前 crate 根开始
相对路径self::module::itemsuper::item从当前模块开始
modfront_of_house{pubmodhosting{pubfnadd_to_waitlist(){}}}fnmain(){// 绝对路径crate::front_of_house::hosting::add_to_waitlist();// 相对路径(从当前模块开始)front_of_house::hosting::add_to_waitlist();}

使用super访问父模块

fnserve_order(){}modback_of_house{fnfix_incorrect_order(){cook_order();super::serve_order();// super 指向父模块}fncook_order(){}}

五、use 关键字

use用于将路径导入作用域,简化重复引用。

modfront_of_house{pubmodhosting{pubfnadd_to_waitlist(){}}}usecrate::front_of_house::hosting;fnmain(){hosting::add_to_waitlist();// 不需要写完整路径}

5.1 使用as重命名

usestd::fmt::Result;usestd::io::ResultasIoResult;// 解决名称冲突

5.2 导入多项

usestd::collections::{HashMap,HashSet,VecDeque};// 等价于// use std::collections::HashMap;// use std::collections::HashSet;// use std::collections::VecDeque;

5.3 通配符*

usestd::collections::*;// 导入所有公共项

谨慎使用,可能造成命名冲突和可读性下降。

5.4 嵌套路径

usestd::{cmp::Ordering,collections::{HashMap,HashSet},io::{self,Write},};fnmain(){letmutmap=HashMap::new();io::stdout().write(b"hello").unwrap();}

六、拆分模块到多个文件

随着项目变大,需要将模块拆分到独立文件中。

6.1 传统方式(Rust 2015 风格,仍可用)

// src/lib.rsmodfront_of_house;// 声明模块,内容在 src/front_of_house.rspubusecrate::front_of_house::hosting;pubfneat_at_restaurant(){hosting::add_to_waitlist();}
// src/front_of_house.rspubmodhosting;// 声明子模块,内容在 src/front_of_house/hosting.rs
// src/front_of_house/hosting.rspubfnadd_to_waitlist(){}

6.2 现代方式(Rust 2018+,推荐)

使用与模块同名的目录 +mod.rs

my_crate/ ├── src/ │ ├── lib.rs │ ├── front_of_house/ │ │ ├── mod.rs # front_of_house 模块内容 │ │ └── hosting.rs # hosting 模块内容
// src/lib.rsmodfront_of_house;pubusecrate::front_of_house::hosting;pubfneat_at_restaurant(){hosting::add_to_waitlist();}
// src/front_of_house/mod.rspubmodhosting;
// src/front_of_house/hosting.rspubfnadd_to_waitlist(){println!("Added to waitlist");}

6.3 另一种现代方式(不推荐 mod.rs)

Rust 2018+ 也支持目录名对应模块,但不推荐:

my_crate/ ├── src/ │ ├── lib.rs │ ├── front_of_house.rs # 模块根 │ └── front_of_house/ # 子模块目录 │ └── hosting.rs

通常使用mod.rs方案更清晰。


七、pub use —— 重导出

pub use既可以导入项,又可以让外部访问它:

// lib.rsmodfront_of_house;pubusecrate::front_of_house::hosting;// 重导出pubfneat_at_restaurant(){hosting::add_to_waitlist();}

外部使用者:

usemy_restaurant::hosting;// 直接使用重导出的模块fnmain(){hosting::add_to_waitlist();}

八、Cargo.toml 与第三方依赖

8.1 添加依赖

[package] name = "my_project" version = "0.1.0" edition = "2021" [dependencies] rand = "0.8.5" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = ["full"] }

8.2 使用第三方 crate

userand::Rng;useserde::{Serialize,Deserialize};fnmain(){letmutrng=rand::thread_rng();letn:u8=rng.gen();println!("随机数: {}",n);}

九、完整项目示例

让我们创建一个餐厅管理系统来演示模块系统:

项目结构

restaurant/ ├── Cargo.toml └── src/ ├── main.rs ├── lib.rs ├── front_of_house/ │ ├── mod.rs │ └── hosting.rs └── back_of_house.rs

Cargo.toml

[package] name = "restaurant" version = "0.1.0" edition = "2021"

src/lib.rs

modfront_of_house;modback_of_house;// 重导出常用项pubusecrate::front_of_house::hosting;pubusecrate::back_of_house::Breakfast;pubfneat_at_restaurant(){// 绝对路径crate::front_of_house::hosting::add_to_waitlist();// 相对路径front_of_house::hosting::seat_at_table();// 使用 Breakfastletmutmeal=Breakfast::summer("Rye");meal.toast=String::from("Wheat");println!("I'd like {} toast please",meal.toast);}

src/front_of_house/mod.rs

pubmodhosting;pubfnseat_at_table(){println!("Seating at table");}

src/front_of_house/hosting.rs

pubfnadd_to_waitlist(){println!("Added to waitlist");}fnseat_at_table(){// 私有函数println!("Seating at table (private)");}

src/back_of_house.rs

pubstructBreakfast{pubtoast:String,// 公有字段seasonal_fruit:String,// 私有字段}implBreakfast{pubfnsummer(toast:&str)->Breakfast{Breakfast{toast:String::from(toast),seasonal_fruit:String::from("peaches"),}}}pubenumAppetizer{// 枚举的变体默认都是公有的Soup,Salad,}

src/main.rs

userestaurant::{hosting,Breakfast};fnmain(){println!("=== 使用 restaurant crate ===");// 使用重导出的 hostinghosting::add_to_waitlist();// 使用 Breakfastletmutmeal=Breakfast::summer("Rye");meal.toast=String::from("Wheat");println!("Toast: {}",meal.toast);// meal.seasonal_fruit = String::from("blueberries"); // 错误:私有字段}

十、常见陷阱与最佳实践

陷阱 1:忘记添加pub

modmy_module{fnprivate_function(){}// 私有,外部无法访问}fnmain(){my_module::private_function();// ❌ 编译错误}

陷阱 2:路径混乱

// 不推荐:深层嵌套的路径usecrate::foo::bar::baz::qux::my_function;// 推荐:使用模块别名或重导出usecrate::foo::bar::baz::qux;qux::my_function();

最佳实践总结

场景推荐做法
小型项目所有代码放在main.rslib.rs
中型项目按功能拆分到同目录的多个.rs文件
大型项目使用目录 +mod.rs分层组织
暴露 API使用pub use重导出,隐藏内部结构
导入依赖Cargo.toml中声明,在代码中使用use

十一、总结与速查

概念语法说明
定义模块mod module_name { ... }内联模块
声明外部模块mod module_name;模块在其他文件
公有性pub使项对外可见
绝对路径crate::module::item从 crate 根开始
相对路径super::itemself::item从父模块或当前模块开始
导入use path::to::item简化路径
重命名use path as alias解决冲突
重导出pub use path导入并对外暴露

核心要点

  • mod定义模块,控制私有性
  • ✅ 路径可以是绝对(crate::)或相对(self::/super::
  • use将路径引入作用域
  • pub use可以重导出 API
  • ✅ 大型项目应将模块拆分到多个文件

十二、思考题

  1. 下面代码能否编译?为什么?
modouter{fnprivate(){}pubmodinner{pubfncall(){super::private();// 子模块能调用父模块的私有函数吗?}}}fnmain(){outer::inner::call();}
  1. mod声明和use导入有什么区别?

  2. 如何让外部 crate 用户只看到my_crate::something,而看不到my_crate::internal::something


参考链接

  • Rust Book - Packages and Crates
  • Rust Book - Modules
  • Rust Book - Paths
  • Rust Book - Use
  • Rust Book - Separating Files
返回列表