Rust: O Sistema de Módulos do Rust e o Cargo
Última atualização: 2026-08-26
O sistema de módulos é o “esquema de endereçamento de código” do Rust — assim como o endereço de um prédio, ele permite identificar com precisão a localização de cada trecho de código e controlar quais partes do código podem ser acessadas externamente.
Se um grande projeto fosse uma cidade, os módulos (mods) seriam os bairros, os arquivos seriam os prédios e as funções seriam os cômodos. O Cargo é a administração da cidade — responsável por construir estradas, realizar manutenção e garantir a qualidade. Sem um sistema de módulos, o código seria uma confusão de “aldeias urbanas”.
1. O que você vai aprender
modMódulos de definição de palavras-chave e módulos aninhadospubControle de visibilidade — Regras de acesso para módulos pai, filho e irmãosuseImportação de caminho esuper/crateCaminhos relativosCargo.tomlGerenciamento de dependências e versionamento semântico (SemVer)cargo build/test/bench/docComandos comuns- Gerenciamento de múltiplos pacotes no espaço de trabalho e a divisão de tarefas entre
lib.rsemain.rs
2. A história por trás do sistema de numeração dos apartamentos
(1) Sofrimento: Uma cidade sem números nas casas
Tom se mudou para um prédio de apartamentos recém-construído e descobriu que ele não tinha um sistema de numeração das residências.
- Ele estava procurando a sala 502 no prédio 3, mas todos os prédios pareciam iguais.
- Ao entregar comida, os entregadores precisam ligar e perguntar: “Em qual prédio você está?”
- Aviso da administração do condomínio: “Por favor, desçam para retirar suas encomendas, moradores da Unidade 3, 5º andar” — mas, na verdade, há seis famílias no 5º andar da Unidade 3.
- Para piorar a situação, os novos moradores mudaram o número da casa para “Alibaba” — e todo o sistema de endereçamento entrou em colapso.
“Seria ótimo se houvesse um sistema padronizado de numeração de residências: prédio-unidade-apartamento...”
(2) Abordagens ao sistema de módulos do Rust
Apartment(Crate) → A building
Building Name(crate name) → Building Number
Unit(Module) → Apartment Building Entrance
Room Number(Function) → Specific Rooms
// File Structure Correspondence:
// src/
// main.rs → Apartment Lobby(Entrance)
// building/
// mod.rs → Building Information
// unit_1/
// mod.rs → 1 Unit
// room_501.rs → 501 Room
// room_502.rs → 502 Room
// In Rust, through mod and pub, precisely control who can access what
mod building {
pub mod unit_1 {
pub fn room_501() -> &'static str {
"501 Room: Tom Home"
}
fn room_502() -> &'static str {
"502 Room: Private Space" // Default: Private, not visible externally
}
}
}
fn main() {
// Access via the full path
println!("{}", building::unit_1::room_501());
// println!("{}", building::unit_1::room_502()); // ❌ Private Functions,Compilation Error
}
O sistema de módulos do Rust é como o sistema de endereçamento de um prédio de apartamentos:
craterepresenta o prédio inteiro,modrepresenta o bloco efnrepresenta o apartamento.pubcontrola quais apartamentos têm portas que se abrem para o exterior; os apartamentos sempubsão privados — pessoas de fora não podem entrar neles livremente.
3. Conceitos fundamentais
(1) Sistema de módulos e caminhos
graph TB
A[Rust Modular System] --> B[mod Definition]
A --> C[pub Visibility]
A --> D[use Path Import]
A --> E[Cargo Project Management]
B --> B1["mod Module Name { ... }"]
B --> B2["mod Module Name; // From a file"]
C --> C1["pub: Visible to the public"]
C --> C2["pub(crate): crate only, visible inside"]
C --> C3["pub(super): Visible only to the parent module"]
C --> C4["No pub: Private"]
D --> D1["use crate::a::b::c;"]
D --> D2["use super::module;"]
D --> D3["use self::module;"]
E --> E1["Cargo.toml Dependency"]
E --> E2["cargo build / test"]
E --> E3["workspace Multiple Packages"]
E --> E4["lib.rs vs main.rs"]
(2) Comparação das regras de visibilidade
| Visibilidade | Palavras-chave | Quem pode acessar | Analogia |
|---|---|---|---|
| Privado | Nenhum (padrão) | Módulo atual e submódulos | Somente membros da família podem entrar no quarto |
| Visível para o módulo pai | pub(super) |
Módulo pai | Os vizinhos do andar de cima e de baixo podem dar uma passada por aqui |
| Visível dentro do crate | pub(crate) |
Todos os módulos no crate atual | Os moradores podem entrar pelo portão da comunidade |
| Público | pub |
Todas as caixas externas | Qualquer pessoa pode entrar no shopping |
(3) Tipos de caminho
| Tipo de caminho | Prefixo | Exemplo | Descrição |
|---|---|---|---|
| Caminho absoluto | crate:: |
crate::utils::helper::foo |
A partir da raiz do crate |
| Caminho relativo | self:: |
self::helper::foo |
A partir do módulo atual |
| Caminho relativo | super:: |
super::helper::foo |
A partir do módulo pai |
| Caminho externo | Nome do pacote | serde::Serialize |
A partir de um crate externo |
(4) Referência rápida para comandos comuns de carga
| Comando | Função | Opções comuns |
|---|---|---|
cargo new |
Criar um novo projeto | --lib (Projeto de biblioteca) |
cargo build |
Compilar projeto | --release (Compilação otimizada) |
cargo run |
Compilar e executar | --bin name (especificar binário) |
cargo check |
Verifica rapidamente se há erros de compilação | Mais rápido do que uma compilação; não gera binários |
cargo test |
Executar teste | test_name (Teste especificado) |
cargo doc |
Gerar documento | --open (Abre o navegador automaticamente) |
cargo clippy |
Verificações de lint de código | -W clippy::all |
cargo fmt |
Formatação de código | --check (Apenas verifique, não altere) |
cargo add |
Adicionar dependência | --features xxx |
cargo update |
Atualizar o arquivo de bloqueio de dependências | Atualizar o Cargo.lock |
cargo publish |
Publicar no crates.io | É preciso fazer login primeiro |
cargo clean |
Limpar artefatos de compilação | Excluir o diretório target/ |
4. Módulos e exemplos do Cargo
▶ Exemplo 1: Definições de módulos e visibilidade pub (Dificuldade ⭐)
// ============================================
// Module Nesting、pub Visibility、Path Access
// Demo: The restaurant's kitchen is not visible to customers, but visible to servers
// ============================================
// Defining the Restaurant Module
mod restaurant {
// Public: Customers may enter the restaurant
pub struct Menu {
pub name: String,
price: f64, // Default: Private,Not visible externally
}
impl Menu {
// Public Constructor
pub fn new(name: &str, price: f64) -> Menu {
Menu {
name: name.to_string(),
price,
}
}
// Public Methods: Get Price
pub fn get_price(&self) -> f64 {
self.price
}
}
// Public: Customers can order food
pub fn order_food(item: &str) -> String {
// Private: Kitchen operations are not visible to customers
let prepared = prepare_in_kitchen(item);
format!("Your {} Ready: {}", item, prepared)
}
// Private: Customers are not allowed in the kitchen.
fn prepare_in_kitchen(item: &str) -> String {
format!("[Kitchen] {} Cooking in progress...", item)
}
// Nested Modules: Inside the Kitchen
mod kitchen {
// Private Storage Area
pub struct Storage {
pub items: Vec<String>,
}
impl Storage {
pub fn new() -> Storage {
Storage {
items: vec![
"Vegetables".to_string(),
"Meat".to_string(),
"Seasonings".to_string(),
],
}
}
}
}
}
fn main() {
// Accessing Public Module Members
let dish = restaurant::order_food("Kung Pao Chicken");
println!("{}", dish);
// Create a public struct
let menu_item = restaurant::Menu::new("Kung Pao Chicken", 38.0);
println!("Dishes: {}, Price: {:.1} yuan", menu_item.name, menu_item.get_price());
// The following code cannot be compiled(Uncomment this line to try it):
// println!("Chef Information: {}", restaurant::prepare_in_kitchen("Kung Pao Chicken")); // ❌ Private Functions
// println!("Price: {}", menu_item.price); // ❌ Private Fields
// let storage = restaurant::kitchen::Storage::new(); // ❌ kitchen The module is private.
}
Resultado:
Your Kung Pao Chicken Ready: [Kitchen] Kung Pao Chicken Cooking in progress...
Dishes: Kung Pao Chicken, Price: 38.0 yuan
As regras de visibilidade dos módulos são como o layout físico de um restaurante: os clientes (código externo) só podem entrar na sala de jantar (módulo
pub) e não podem entrar na cozinha (módulos privados). As operações que ocorrem na cozinha (prepare_in_kitchen) são completamente invisíveis para o mundo exterior — isso é encapsulamento.
▶ Exemplo 2: caminhos use e super/crate (Dificuldade: ⭐⭐)
// ============================================
// use Keyword Import Path、super and crate Relative Path
// Simulation: Company Organizational Structure - Department→Group→Employees
// ============================================
// Top-Level Module: Company
mod company {
// Engineering Department
pub mod engineering {
pub fn team_name() -> &'static str {
"Engineering Department"
}
// Front-End Team
pub mod frontend {
pub fn member_count() -> u32 {
5
}
// Use super to access the parent module (engineering)
pub fn full_info() -> String {
format!("{} Front-End Team, {} people", super::team_name(), member_count())
}
}
// Backend Team
pub mod backend {
pub fn member_count() -> u32 {
8
}
// Usage super Access the Parent Module
pub fn full_info() -> String {
format!("{} Backend Team, {} people", super::team_name(), member_count())
}
}
}
// Marketing Department
pub mod marketing {
pub fn team_name() -> &'static str {
"Marketing Department"
}
// Usage crate Path Access from the Root
pub fn total_employees() -> u32 {
// From crate root, access begins
crate::company::engineering::frontend::member_count()
+ crate::company::engineering::backend::member_count()
+ self::member_count()
}
fn member_count() -> u32 {
6
}
}
}
// Usage use Import Path——Simplify the call
use company::engineering::frontend;
use company::engineering::backend;
use company::marketing;
fn main() {
// Method 1: Full path (Not recommended, too long to write)
println!("{}", company::engineering::frontend::full_info());
// Method 2: Call directly after use import (Recommended)
println!("{}", frontend::full_info());
println!("{}", backend::full_info());
// Introduction marketing Module
println!("Number of employees in the Marketing Department: {}", marketing::member_count());
// Usage crate Path Access
println!("Total Number of Employees: {}", marketing::total_employees());
// Usage as Avoiding Alias Conflicts
use company::engineering as eng;
println!("Department: {}", eng::team_name());
}
Resultado:
Engineering Department Front-End Team, 5 people
Engineering Department Backend Team, 8 people
Number of employees in the Marketing Department: 6
Total Number of Employees: 19
Department: Engineering Department
useÉ como criar um atalho para um “número de casa” — assim, você não precisa digitar o endereço completo todas as vezescompany::engineering::frontend::full_info().supersignifica “subir um nível” (módulo pai), ecratesignifica “voltar à entrada do prédio” (raiz da caixa).asPalavras-chave podem ser usadas para criar aliases de caminhos, resolvendo conflitos causados por nomes duplicados.
▶ Exemplo 3: Gerenciamento de dependências e estrutura do projeto no Cargo.toml (Dificuldade: ⭐⭐)
// ============================================
// Simulation Cargo Project Structure + Dependency Management
// Demo:lib.rs and main.rs Division of Labor、Using External Dependencies
// ============================================
// Note: This example demonstrates the code in lib.rs
// Actual Cargo.toml See the note below for the contents of the document.
// ============================================
// Cargo.toml Content (Simulation):
// ============================================
// [package]
// name = "my-toolkit"
// version = "0.1.0"
// edition = "2021"
//
// [dependencies]
// serde = { version = "1.0", features = ["derive"] }
// serde_json = "1.0"
// chrono = "0.4"
// regex = "1.10"
//
// [dev-dependencies]
// rand = "0.8"
//
// [profile.release]
// opt-level = 3
// ============================================
// Tools Module: Date Handling
pub mod date_utils {
pub fn format_today() -> String {
// Used in actual projects chrono::Local::now()
"2026-07-03".to_string()
}
pub fn is_weekend(day: &str) -> bool {
day.ends_with("Saturday") || day.ends_with("Sunday")
}
}
// Tools Module: String Processing
pub mod string_utils {
/// Verify the email address format (Simulating Regular Expression Matching)
pub fn validate_email(email: &str) -> bool {
// Simplified Verification: Use regex crate in practice
email.contains('@') && email.contains('.')
}
/// Remove non-alphanumeric characters (Simulation)
pub fn sanitize(input: &str) -> String {
input.chars()
.filter(|c| c.is_alphanumeric() || *c == ' ')
.collect()
}
}
// Tools Module: Mathematical Calculations
pub mod math_utils {
/// Calculate the nth term of the nth term of the Fibonacci sequence
pub fn fibonacci(n: u32) -> u64 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
/// Determining Whether a Number Is Prime
pub fn is_prime(n: u32) -> bool {
if n < 2 {
return false;
}
let limit = (n as f64).sqrt() as u32;
for i in 2..=limit {
if n % i == 0 {
return false;
}
}
true
}
}
// Test Module (Using #[cfg(test)] Conditional Compilation)
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_email() {
assert!(string_utils::validate_email("user@example.com"));
assert!(!string_utils::validate_email("invalid"));
}
#[test]
fn test_fibonacci() {
assert_eq!(math_utils::fibonacci(0), 0);
assert_eq!(math_utils::fibonacci(1), 1);
assert_eq!(math_utils::fibonacci(10), 55);
}
#[test]
fn test_is_prime() {
assert!(math_utils::is_prime(17));
assert!(!math_utils::is_prime(1));
assert!(!math_utils::is_prime(4));
}
#[test]
fn test_sanitize() {
assert_eq!(string_utils::sanitize("hello@world!"), "hello world");
}
}
// ============================================
// main.rs The code in (Simulation):
// ============================================
// use my_toolkit::{
// date_utils,
// string_utils,
// math_utils,
// };
//
// fn main() {
// println!("Today's Date: {}", date_utils::format_today());
// println!("Email Verification: {}", string_utils::validate_email("test@example.com"));
// println!("Prime Number Check: {}", math_utils::is_prime(17));
// }
fn main() {
// Demonstration of Each Tool's Functions
println!("=== Tool Library Demo ===");
// Date Tools
println!("Today: {}", date_utils::format_today());
println!("Is it the weekend?: {}", date_utils::is_weekend("Saturday"));
// String Tools
println!("Email Verification test@example.com: {}", string_utils::validate_email("test@example.com"));
println!("Email Verification invalid: {}", string_utils::validate_email("invalid"));
println!("Purification 'hello@world!': {}", string_utils::sanitize("hello@world!"));
// Mathematical Tools
println!("Fibonacci #10: {}", math_utils::fibonacci(10));
println!("17 Is it a prime number?: {}", math_utils::is_prime(17));
println!("4 Is it a prime number?: {}", math_utils::is_prime(4));
println!("=== End of Presentation ===");
// Instructions for Running the Test (Use cargo test in actual projects)
println!("Usage `cargo test` Run Unit Tests");
}
Resultado:
=== Tool Library Demo ===
Today: 2026-07-03
Is it the weekend?: true
Email Verification test@example.com: true
Email Verification invalid: false
Purification 'hello@world!': hello world
Fibonacci #10: 55
17 Is it a prime number?: true
4 Is it a prime number?: false
=== End of Presentation ===
Usage `cargo test` Run Unit Tests
Estrutura padrão para projetos reais:
lib.rscontém o código da biblioteca (API pública),main.rscontém o ponto de entrada do executável (que utiliza a biblioteca).Cargo.tomlgerencia as dependências,[dependencies]contém as dependências de produção e[dev-dependencies]contém as dependências de ferramentas de teste/compilação.cargo testdetecta e executa automaticamente as funções marcadas com#[test].
▶ Exemplo 4: Comandos e áreas de trabalho comuns do Cargo (Dificuldade ⭐⭐⭐)
// ============================================
// Cargo Common Commands and workspace Multi-Package Management
// Simulation: One "Task Manager" workspace Project
// ============================================
// ============================================
// Top Floor Cargo.toml (workspace):
// ============================================
// [workspace]
// members = [
// "task-core", // Core Library
// "task-cli", // CLI Tools
// "task-web", // Web Interface
// ]
//
// [workspace.package]
// version = "1.0.0"
// edition = "2021"
// ============================================
// ============================================
// task-core/Cargo.toml:
// ============================================
// [package]
// name = "task-core"
// version.workspace = true
// edition.workspace = true
//
// [dependencies]
// serde = { version = "1.0", features = ["derive"] }
// chrono = "0.4"
// ============================================
// Simulation task-core Library code
pub mod task_core {
use std::collections::HashMap;
/// Task Priority
#[derive(Debug, Clone, PartialEq)]
pub enum Priority {
Low,
Medium,
High,
Urgent,
}
/// Task Status
#[derive(Debug, Clone, PartialEq)]
pub enum Status {
Todo,
InProgress,
Done,
Cancelled,
}
/// Core Task Structure
#[derive(Debug, Clone)]
pub struct Task {
pub id: u64,
pub title: String,
pub priority: Priority,
pub status: Status,
pub tags: Vec<String>,
}
impl Task {
pub fn new(id: u64, title: &str, priority: Priority) -> Task {
Task {
id,
title: title.to_string(),
priority,
status: Status::Todo,
tags: Vec::new(),
}
}
pub fn add_tag(&mut self, tag: &str) {
self.tags.push(tag.to_string());
}
pub fn is_completed(&self) -> bool {
self.status == Status::Done || self.status == Status::Cancelled
}
}
/// Task Manager
pub struct TaskManager {
tasks: HashMap<u64, Task>,
next_id: u64,
}
impl TaskManager {
pub fn new() -> TaskManager {
TaskManager {
tasks: HashMap::new(),
next_id: 1,
}
}
pub fn create_task(&mut self, title: &str, priority: Priority) -> u64 {
let id = self.next_id;
self.next_id += 1;
let task = Task::new(id, title, priority);
self.tasks.insert(id, task);
id
}
pub fn get_task(&self, id: u64) -> Option<&Task> {
self.tasks.get(&id)
}
pub fn complete_task(&mut self, id: u64) -> bool {
if let Some(task) = self.tasks.get_mut(&id) {
task.status = Status::Done;
true
} else {
false
}
}
pub fn list_tasks(&self) -> Vec<&Task> {
let mut tasks: Vec<&Task> = self.tasks.values().collect();
tasks.sort_by_key(|t| t.id);
tasks
}
}
}
// ============================================
// Cargo Command Reference (Demonstrated in the comments):
// ============================================
// Common Commands:
// cargo new project_name -- Create a New Project
// cargo build -- Compilation (debug)
// cargo build --release -- Compilation (release optimization)
// cargo run -- Compilation + Run
// cargo check -- Quickly Check for Compilation Errors (No binary files generated)
// cargo test -- Run Test
// cargo test test_name -- Run the specified test
// cargo bench -- Run Performance Benchmarks
// cargo doc --open -- Generate the document and open it
// cargo clippy -- Code lint Inspection
// cargo fmt -- Code Formatting
// cargo add crate_name -- Add Dependencies
// cargo update -- Update Dependencies
// cargo publish -- Post to crates.io
// cargo clean -- Clean up compilation output
//
// Workspace Commands:
// cargo build --workspace -- Compilation workspace All packages in
// cargo test -p task-core -- Test only the specified package
// cargo run -p task-cli -- Run the specified package
fn main() {
use task_core::{Priority, TaskManager};
println!("=== Task Manager (Simulation Workspace Project) ===");
let mut manager = TaskManager::new();
// Create a Task
let id1 = manager.create_task("Study Rust Smart Pointers", Priority::High);
let id2 = manager.create_task("Complete the module system exercises", Priority::Medium);
let id3 = manager.create_task("Restore the Production Environment Bug", Priority::Urgent);
// List all tasks
println!("\n--- All Tasks ---");
for task in manager.list_tasks() {
println!("#{} [{:?}] {} - {:?}", task.id, task.priority, task.title, task.status);
}
// Complete a task
manager.complete_task(id1);
println!("\nDone #{} after:", id1);
for task in manager.list_tasks() {
let status = if task.is_completed() { "Completed" } else { "In progress" };
println!("#{} {} - {}", task.id, task.title, status);
}
// Get a Single Task
if let Some(task) = manager.get_task(id3) {
println!("\nUrgent Task: #{} {} ({:?})", task.id, task.title, task.priority);
}
println!("\n=== End of Presentation ===");
println!("Project Structure: task-core (Library) + task-cli (CLI) + task-web (Web)");
println!("Usage `cargo test -p task-core` Testing the Core Library");
println!("Usage `cargo doc --open` Generate Document");
}
Resultado:
=== Task Manager (Simulation Workspace Project) ===
--- All Tasks ---
#1 [High] Study Rust Smart Pointers - Todo
#2 [Medium] Complete the module system exercises - Todo
#3 [Urgent] Restore the Production Environment Bug - Todo
Done #1 after:
#1 Study Rust Smart Pointers - Completed
#2 Complete the module system exercises - In progress
#3 Restore the Production Environment Bug - In progress
Urgent Task: #3 Restore the Production Environment Bug (Urgent)
=== End of Presentation ===
Project Structure: task-core (Library) + task-cli (CLI) + task-web (Web)
Usage `cargo test -p task-core` Testing the Core Library
Usage `cargo doc --open` Generate Document
O Workspace é uma ferramenta poderosa para gerenciar projetos com vários pacotes:
task-corefornece os tipos e a lógica principais (bibliotecas),task-clifornece a interface de linha de comando (executável),task-webfornece a API da Web (outro executável) ecargo build --workspacecompila todos os pacotes de uma só vez. Ocargo test -p task-coretesta apenas as bibliotecas principais.
▶ Exemplo 5: Exercício abrangente — Simulação de projeto modular (Dificuldade ⭐⭐⭐)
// ============================================
// Comprehensive Example: Module Visibility and API Design
// Simulating a Multi-File Project Structure (Actual projects should be broken down into separate files.)
// ============================================
mod math_utils {
pub fn add(a: i32, b: i32) -> i32 { a + b }
pub fn multiply(a: i32, b: i32) -> i32 { a * b }
fn internal_check(val: i32) -> bool { val >= 0 }
pub fn safe_divide(a: i32, b: i32) -> Option<i32> {
if b == 0 { return None; }
if !internal_check(a) || !internal_check(b) { return None; }
Some(a / b)
}
pub mod constants {
pub const PI: f64 = 3.14159265358979;
pub const E: f64 = 2.71828182845905;
pub const MAX_I32: i32 = i32::MAX;
}
}
mod string_utils {
pub fn capitalize(s: &str) -> String {
let mut chars = s.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
}
}
pub fn truncate(s: &str, max_len: usize) -> String {
if s.len() <= max_len { s.to_string() }
else { format!("{}...", &s[..max_len.min(s.len())]) }
}
}
mod user {
pub struct User {
pub name: String,
age: u8,
email: String,
}
impl User {
pub fn new(name: &str, age: u8, email: &str) -> Self {
User { name: name.to_string(), age, email: email.to_string() }
}
pub fn age(&self) -> u8 { self.age }
pub fn summary(&self) -> String {
format!("{} ({} years old, {})", self.name, self.age, self.email)
}
}
}
fn main() {
use math_utils::{add, multiply, safe_divide, constants};
use string_utils::{capitalize, truncate};
use user::User;
println!("=== math_utils Module ===");
println!("2 + 3 = {}", add(2, 3));
println!("4 * 5 = {}", multiply(4, 5));
println!("10 / 3 = {:?}", safe_divide(10, 3));
println!("10 / 0 = {:?}", safe_divide(10, 0));
println!("PI = {:.5}, E = {:.5}", constants::PI, constants::E);
println!("\n=== string_utils Module ===");
println!("capitalize: '{}'", capitalize("rust"));
println!("truncate: '{}'", truncate("Hello, World!", 8));
println!("\n=== user Module ===");
let alice = User::new("Alice", 30, "alice@example.com");
println!("{}", alice.summary());
println!("Age: {}", alice.age());
}
Resultado:
=== math_utils Module ===
2 + 3 = 5
4 * 5 = 20
10 / 3 = Some(3)
10 / 0 = None
PI = 3.14159, E = 2.71828
=== string_utils Module ===
capitalize: 'Rust'
truncate: 'Hello, ...'
=== user Module ===
Alice (30 years old, alice@example.com)
Age: 30
Três princípios do design modular:
pubExpor apenas as APIs necessárias (comoaddesafe_divide) e manter os detalhes internos (comointernal_check) privados; Os submódulos (comoconstants) são expostos por meio depub mod; os campos da estrutura são anotados individualmente com visibilidade (privadopub name/age+ getterage()).
❓ Perguntas Frequentes
P: Qual é a diferença entre
modefn? Por que não organizá-los usando arquivos? R:modé uma definição de módulo, efné uma definição de função. O Rust suporta duas abordagens:mod xxx { ... }inline ou arquivosmod xxx;(carregados a partir dexxx.rsouxxx/mod.rs). A abordagem baseada em arquivos oferece maior clareza em projetos grandes, mas as regras de visibilidade de um módulo não são afetadas pela estrutura do arquivo — elas são controladas exclusivamente pela palavra-chavepub.
P: Qual é a diferença entre
pub(crate)epub? R:pub(crate)é visível apenas para o código dentro do mesmo crate, enquantopubé visível para todos os crates externos. Se você estiver escrevendo uma biblioteca, as funções auxiliares internas devem usarpub(crate)em vez depub, para que usuários externos não vejam APIs internas que não devem ser utilizadas.
P: Quando se deve usar
use super::xxxeuse crate::xxx? R:superé usado para acessar o módulo pai (caminho relativo), ecrateé usado para acessar o conteúdo a partir da raiz do crate (caminho absoluto). Recomendamos o uso decrate(caminhos absolutos), pois os caminhos relativos são propensos a erros durante a refatoração.superé usado principalmente em submódulos para acessar rapidamente o conteúdo do módulo pai.
P: O que significa o número de versão
^1.2.3no Cargo.toml? R:^significa “atualização de compatibilidade” — uma versão que permite>=1.2.3e<2.0.0. Essa é a regra padrão de versionamento semântico (SemVer) do Cargo:^1.2.3permite atualizações compatíveis com versões anteriores dentro do intervalo 1.x.x.=1.2.3fixa a versão exata, e>=1.2.3permite qualquer versão superior.
P:
lib.rsemain.rspodem coexistir? R: Sim.lib.rsdefine a API pública da biblioteca,main.rsfunciona como ponto de entrada do executável —main.rsimporta o código delib.rspor meio deuse crate_name::xxx. Esse é um padrão comum em projetos Rust: seu código é colocado emlib.rspara facilitar testes e reutilização, enquantomain.rsserve apenas como um ponto de entrada simples.
📖 Resumo
modMódulo de definição de palavras-chave; pode ser aninhado (mod outer { mod inner { ... } }) ou carregado a partir de um arquivo (mod xxx;)pubControle de visibilidade: Privado por padrão,pubpúblico,pub(crate)visível apenas dentro da crate,pub(super)visível apenas para o módulo paiuseSimplifica as chamadas ao introduzir caminhos; suporta dois tipos de caminhos relativos/absolutos:super(módulo pai) ecrate(módulo raiz)Cargo.tomlGerenciar dependências usando o controle de versão semântico (SemVer); separar[dependencies]de[dev-dependencies]cargo build/test/doc/benchsão os comandos principais do Cargo, enquantocargo clippyecargo fmtgarantem a qualidade do código- Área de trabalho O gerenciamento de múltiplos pacotes utiliza a configuração
Cargo.tomlno nível superior;[workspace]lista todos os subpacotes
📝 Exercícios
- Dificuldade ⭐: Crie um programa contendo dois módulos,
mathegreeting. O módulomathpossui uma função públicaadd(a: i32, b: i32) -> i32, e o módulogreetingpossui uma função públicasay_hello(name: &str) -> String. Chame ambas as funções emmain. - Dificuldade ⭐⭐: Simule um sistema de módulos do tipo “biblioteca”. Crie o módulo
library, que inclui o submódulobooks(gerenciamento de livros) e o submódulomembers(gerenciamento de membros). Obookscontém as funçõesadd_bookelist_books, e omemberscontém as funçõesadd_memberelist_members. Usepub(super)epub(crate)para controlar a visibilidade adequadamente. O módulomaindemonstra como adicionar livros e membros. - Dificuldade ⭐⭐⭐: Explore a estrutura do projeto do espaço de trabalho do Cargo. Crie localmente um projeto de espaço de trabalho que inclua
core-lib(uma biblioteca que fornece as funçõesaddesubtract) ecli-app(um executável que realiza cálculos usandocore-libe imprime os resultados). Configure as definições do espaço de trabalho paraCargo.toml, compile-o usandocargo build --workspacee execute-o usandocargo run -p cli-app.