Crate 与模块
Rust 模块 vs Python 包
Section titled “Rust 模块 vs Python 包”学习内容:
mod和usevsimport、可见性(pub)vs Python 的约定式私有、Cargo.tomlvspyproject.toml、crates.iovs PyPI,以及工作空间 vs 单体仓库。难度: 🟢 初级
Python 模块系统
Section titled “Python 模块系统”# Python — 文件是模块,带 __init__.py 的目录是包
# ├── __init__.py # 使其成为包# ├── main.py# ├── utils/# │ ├── __init__.py # 使 utils 成为子包# │ ├── helpers.py# │ └── validators.py# └── models/# ├── __init__.py# ├── user.py# └── product.py
# 导入方式:from myproject.utils.helpers import format_namefrom myproject.models.user import Userimport myproject.utils.validators as validatorsRust 模块系统
Section titled “Rust 模块系统”// Rust — mod 声明创建模块树,文件提供内容
// ├── main.rs # Crate 根 — 声明模块// ├── utils/// │ ├── mod.rs # 模块声明(类似 __init__.py)// │ ├── helpers.rs// │ └── validators.rs// └── models/// ├── mod.rs// ├── user.rs// └── product.rs
// 在 src/main.rs 中:mod utils; # 告诉 Rust 查找 src/utils/mod.rsmod models; # 告诉 Rust 查找 src/models/mod.rs
use utils::helpers::format_name;use models::user::User;
// 在 src/utils/mod.rs 中:pub mod helpers; # 声明并重新导出 helpers.rspub mod validators; # 声明并重新导出 validators.rsgraph TD A["main.rs<br/>(crate root)"] --> B["mod utils"] A --> C["mod models"] B --> D["utils/mod.rs"] D --> E["helpers.rs"] D --> F["validators.rs"] C --> G["models/mod.rs"] G --> H["user.rs"] G --> I["product.rs"] style A fill:#d4edda,stroke:#28a745 style D fill:#fff3cd,stroke:#ffc107 style G fill:#fff3cd,stroke:#ffc107Python 等价物:把
mod.rs想象成__init__.py——它声明模块导出什么。 Crate 根(main.rs/lib.rs)类似于顶层包的__init__.py。
| 概念 | Python | Rust |
|---|---|---|
| 模块 = 文件 | 自动 | 必须用 mod 声明 |
| 包 = 目录 | __init__.py | mod.rs |
| 默认公开 | 所有 | 默认私有 |
| 设为公开 | _前缀 约定 | pub 关键字 |
| 导入语法 | from x import y | use x::y; |
| 通配符导入 | from x import * | use x::*;(不推荐) |
| 相对导入 | from . import sibling | use super::sibling; |
| 重新导出 | __all__ 或显式 | pub use inner::Thing; |
可见性——默认私有
Section titled “可见性——默认私有”# Python — "我们都是成年人"class User: def __init__(self): self.name = "Alice" # 公开(按约定) self._age = 30 # "私有"(约定:单下划线) self.__secret = "shhh" # 名称修饰(不是真的私有)
# 没什么能阻止你访问 _age 或甚至 __secretprint(user._age) # 正常工作print(user._User__secret) # 也能工作(名称修饰)// Rust — 私有由编译器强制pub struct User { pub name: String, // 公开 — 任何人都可访问 age: i32, // 私有 — 只有这个模块能访问}
impl User { pub fn new(name: &str, age: i32) -> Self { User { name: name.to_string(), age } }
pub fn age(&self) -> i32 { // 公开 getter self.age }
fn validate(&self) -> bool { // 私有方法 self.age > 0 }}
// 模块外:let user = User::new("Alice", 30);println!("{}", user.name); // 公开字段 — 可以// println!("{}", user.age); // 编译错误:字段是私有的println!("{}", user.age()); // 公开方法(getter)— 可以Crate vs PyPI 包
Section titled “Crate vs PyPI 包”Python 包(PyPI)
Section titled “Python 包(PyPI)”# Pythonpip install requests # 从 PyPI 安装pip install "requests>=2.28" # 版本约束pip freeze > requirements.txt # 锁定版本pip install -r requirements.txt # 复现环境Rust Crate(crates.io)
Section titled “Rust Crate(crates.io)”# Rustcargo add reqwest # 从 crates.io 安装(添加到 Cargo.toml)cargo add reqwest@0.12 # 版本约束# Cargo.lock 自动生成 — 无需手动步骤cargo build # 下载并编译依赖Cargo.toml vs pyproject.toml
Section titled “Cargo.toml vs pyproject.toml”# Rust — Cargo.toml[package]name = "my-project"version = "0.1.0"edition = "2021"
[dependencies]serde = { version = "1.0", features = ["derive"] } # 带特性标志reqwest = { version = "0.12", features = ["json"] }tokio = { version = "1", features = ["full"] }log = "0.4"
[dev-dependencies]mockall = "0.13"Python 开发者必备 Crate
Section titled “Python 开发者必备 Crate”| Python 库 | Rust Crate | 用途 |
|---|---|---|
requests | reqwest | HTTP 客户端 |
json (stdlib) | serde_json | JSON 解析 |
pydantic | serde | 序列化/验证 |
pathlib | std::path (stdlib) | 路径处理 |
os / shutil | std::fs (stdlib) | 文件操作 |
re | regex | 正则表达式 |
logging | tracing / log | 日志 |
click / argparse | clap | CLI 参数解析 |
asyncio | tokio | 异步运行时 |
datetime | chrono | 日期和时间 |
pytest | 内置 + rstest | 测试 |
dataclasses | #[derive(...)] | 数据结构 |
typing.Protocol | Trait | 结构化类型 |
subprocess | std::process (stdlib) | 运行外部命令 |
sqlite3 | rusqlite | SQLite |
sqlalchemy | diesel / sqlx | ORM / SQL 工具包 |
fastapi | axum / actix-web | Web 框架 |
工作空间 vs 单体仓库
Section titled “工作空间 vs 单体仓库”Python 单体仓库(典型)
Section titled “Python 单体仓库(典型)”# Python 单体仓库(各种方案,无标准)myproject/├── pyproject.toml # 根项目├── packages/│ ├── core/│ │ ├── pyproject.toml # 每个包有自己的配置│ │ └── src/core/...│ ├── api/│ │ ├── pyproject.toml│ │ └── src/api/...│ └── cli/│ ├── pyproject.toml│ └── src/cli/...# 工具:poetry workspaces、pip -e .、uv workspaces — 无标准Rust 工作空间
Section titled “Rust 工作空间”# Rust — 根目录的 Cargo.toml[workspace]members = [ "core", "api", "cli",]
# 跨工作空间共享依赖[workspace.dependencies]serde = { version = "1.0", features = ["derive"] }tokio = { version = "1", features = ["full"] }# Rust 工作空间结构 — 标准化,内置在 Cargo 中myproject/├── Cargo.toml # 工作空间根目录├── Cargo.lock # 所有 crate 的单一锁文件├── core/│ ├── Cargo.toml # [dependencies] serde.workspace = true│ └── src/lib.rs├── api/│ ├── Cargo.toml│ └── src/lib.rs└── cli/ ├── Cargo.toml └── src/main.rs# 工作空间命令cargo build # 构建一切cargo test # 测试一切cargo build -p core # 只构建 core cratecargo test -p api # 只测试 api cratecargo clippy --all # 检查一切核心洞见:Rust 工作空间是一等公民,内置在 Cargo 中。 Python 单体仓库需要第三方工具(poetry、uv、pants),支持程度各异。 在 Rust 工作空间中,所有 crate 共享一个
Cargo.lock,确保项目范围内的依赖版本一致。
练习:模块可见性(点击展开)
挑战:给定以下模块结构,预测哪些行能编译,哪些不能:
mod kitchen { fn secret_recipe() -> &'static str { "42 spices" } pub fn menu() -> &'static str { "Today's special" }
pub mod staff { pub fn cook() -> String { format!("Cooking with {}", super::secret_recipe()) } }}
fn main() { println!("{}", kitchen::menu()); // A 行 println!("{}", kitchen::secret_recipe()); // B 行 println!("{}", kitchen::staff::cook()); // C 行}答案
- A 行:能编译 —
menu()是pub - B 行:编译错误 —
secret_recipe()对kitchen是私有的 - C 行:能编译 —
staff::cook()是pub,且cook()可以通过super::访问secret_recipe()(子模块可以访问父模块的私有项)
关键收获:在 Rust 中,子模块可以看到父模块的私有项(类似 Python 的 _private 约定,但强制执行)。
外部人员不能。这与 Python 相反,Python 的 _private 只是一个提示。