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.tomlsrc/lib.rssrc/front_of_house/mod.rssrc/front_of_house/hosting.rssrc/back_of_house.rssrc/main.rs十、常见陷阱与最佳实践陷阱 1忘记添加 pub陷阱 2路径混乱最佳实践总结十一、总结与速查十二、思考题参考链接返回首页 | 上篇 | 下篇掌握 Rust 的模块化组织 —— 包、Crate、模块、use 以及项目结构最佳实践一、回顾与导入前面十篇文章我们学习了 Rust 的基础语法和核心特性。随着项目规模增长我们需要一种方式来组织代码将功能拆分到不同的文件中并控制哪些内容对外可见。这就是 Rust 的模块系统的职责。 Rust 的模块系统帮助你组织代码、控制私有性、管理依赖。二、核心概念Rust 的模块系统包含以下几个层级从大到小概念说明示例包Package一个或多个 Crate有Cargo.tomlcargo new my_projectCrate一个编译单元产生一个库或可执行文件lib.rs或main.rs模块Module控制作用域和私有性的代码组织单元mod network路径Path引用模块中的项crate::network::connect什么是 CrateCrate 是 Rust 的编译单元。rustc每次编译处理的都是一个 Crate。Crate 可以生成二进制程序有main函数库可被其他项目使用没有main函数什么是 PackagePackage 包含一个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::item或super::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(){letmutmapHashMap::new();io::stdout().write(bhello).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.rsmy_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.rsRust 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 使用第三方 crateuserand::Rng;useserde::{Serialize,Deserialize};fnmain(){letmutrngrand::thread_rng();letn:u8rng.gen();println!(随机数: {},n);}九、完整项目示例让我们创建一个餐厅管理系统来演示模块系统项目结构restaurant/ ├── Cargo.toml └── src/ ├── main.rs ├── lib.rs ├── front_of_house/ │ ├── mod.rs │ └── hosting.rs └── back_of_house.rsCargo.toml[package] name restaurant version 0.1.0 edition 2021src/lib.rsmodfront_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();// 使用 BreakfastletmutmealBreakfast::summer(Rye);meal.toastString::from(Wheat);println!(Id like {} toast please,meal.toast);}src/front_of_house/mod.rspubmodhosting;pubfnseat_at_table(){println!(Seating at table);}src/front_of_house/hosting.rspubfnadd_to_waitlist(){println!(Added to waitlist);}fnseat_at_table(){// 私有函数println!(Seating at table (private));}src/back_of_house.rspubstructBreakfast{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.rsuserestaurant::{hosting,Breakfast};fnmain(){println!( 使用 restaurant crate );// 使用重导出的 hostinghosting::add_to_waitlist();// 使用 BreakfastletmutmealBreakfast::summer(Rye);meal.toastString::from(Wheat);println!(Toast: {},meal.toast);// meal.seasonal_fruit String::from(blueberries); // 错误私有字段}十、常见陷阱与最佳实践陷阱 1忘记添加pubmodmy_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.rs或lib.rs中型项目按功能拆分到同目录的多个.rs文件大型项目使用目录 mod.rs分层组织暴露 API使用pub use重导出隐藏内部结构导入依赖在Cargo.toml中声明在代码中使用use十一、总结与速查概念语法说明定义模块mod module_name { ... }内联模块声明外部模块mod module_name;模块在其他文件公有性pub使项对外可见绝对路径crate::module::item从 crate 根开始相对路径super::item或self::item从父模块或当前模块开始导入use path::to::item简化路径重命名use path as alias解决冲突重导出pub use path导入并对外暴露核心要点✅mod定义模块控制私有性✅ 路径可以是绝对crate::或相对self::/super::✅use将路径引入作用域✅pub use可以重导出 API✅ 大型项目应将模块拆分到多个文件十二、思考题下面代码能否编译为什么modouter{fnprivate(){}pubmodinner{pubfncall(){super::private();// 子模块能调用父模块的私有函数吗}}}fnmain(){outer::inner::call();}mod声明和use导入有什么区别如何让外部 crate 用户只看到my_crate::something而看不到my_crate::internal::something参考链接Rust Book - Packages and CratesRust Book - ModulesRust Book - PathsRust Book - UseRust Book - Separating Files