Module trong Rust
Mọi ví dụ cho đến giờ đều nằm gọn trong một file main.rs. Với chương trình nhỏ thì vậy là ổn, nhưng dự án thật sẽ chia code thành những phần hợp lý. Công cụ của Rust cho việc đó là hệ thống module — mod, pub và use.
Định nghĩa một module
Phần tiêu đề “Định nghĩa một module”Module là một khối có tên, gom các đoạn code liên quan lại với nhau:
mod math { pub fn add(a: i32, b: i32) -> i32 { a + b }}
fn main() { let sum = math::add(2, 3); println!("{sum}");}Kết quả:
5Hai điều cần để ý: hàm được gọi bằng math::add(...), dùng tên module như một đường dẫn (path). Và nó được đánh dấu pub — nếu không có, sẽ không thể gọi nó từ bên ngoài module.
Mọi thứ mặc định là riêng tư
Phần tiêu đề “Mọi thứ mặc định là riêng tư”Điều này đáng nói rõ vì nó khiến nhiều người bất ngờ: nội dung của module mặc định là riêng tư (private). Nếu bạn bỏ pub khỏi ví dụ trên, nó sẽ không biên dịch được:
mod math { fn add(a: i32, b: i32) -> i32 { // không có `pub` a + b }}
fn main() { let sum = math::add(2, 3); // ❌ lỗi: hàm `add` là riêng tư println!("{sum}");}Bạn phải đánh dấu pub một cách tường minh cho mọi thứ mà bạn muốn code bên ngoài module được dùng. Đây là chủ ý — nó cho phép module giấu các hàm phụ trợ bên trong và chỉ lộ ra những phần được thiết kế làm API công khai.
Module lồng nhau
Phần tiêu đề “Module lồng nhau”Module có thể chứa module khác, đó là cách các dự án lớn xây dựng nên một cấu trúc dạng cây:
mod shapes { pub mod circle { pub fn area(radius: f64) -> f64 { 3.14159 * radius * radius } }}
fn main() { let area = shapes::circle::area(2.0); println!("{area}");}use — rút gọn đường dẫn dài
Phần tiêu đề “use — rút gọn đường dẫn dài”Lần nào cũng viết shapes::circle::area(...) thì thật mệt. use đưa một đường dẫn vào phạm vi để bạn gọi nó trực tiếp:
mod shapes { pub mod circle { pub fn area(radius: f64) -> f64 { 3.14159 * radius * radius } }}
use shapes::circle;
fn main() { let area = circle::area(2.0); // ngắn hơn shapes::circle::area println!("{area}");}Thật ra bạn đã dùng use rồi — đó chính là từ khóa trong bài HashMap (use std::collections::HashMap;).
Tách module ra nhiều file
Phần tiêu đề “Tách module ra nhiều file”Các ví dụ ở trên đặt mọi thứ trong một file, vì Rust Playground yêu cầu như vậy. Trong một dự án thật trên máy của bạn, mỗi module thường có file riêng. Nếu bạn có một dự án với module math, nó thường trông như thế này:
src/├── main.rs└── math.rsmod math; // báo cho Rust: tìm code của module này trong math.rs
fn main() { let sum = math::add(2, 3); println!("{sum}");}pub fn add(a: i32, b: i32) -> i32 { a + b}mod math; (có dấu chấm phẩy, không có thân) bảo Rust nạp nội dung module từ math.rs trong cùng thư mục. Đây là mẫu bạn sẽ dùng ngay khi dự án lớn hơn một file.
Luyện tập
Phần tiêu đề “Luyện tập”1. Module nhiệt độ
Phần tiêu đề “1. Module nhiệt độ”Tạo module tên convert với hai hàm công khai: c_to_f (độ C sang độ F) và f_to_c (chiều ngược lại). Từ main, in ra 100°C là 212°F và 212°F là 100°C.
Gợi ý: công thức là c * 9.0 / 5.0 + 32.0 và (f - 32.0) * 5.0 / 9.0. Đừng quên pub, và gọi các hàm kèm tên module, như convert::c_to_f(100.0).
Xem lời giải
mod convert { pub fn c_to_f(c: f64) -> f64 { c * 9.0 / 5.0 + 32.0 }
pub fn f_to_c(f: f64) -> f64 { (f - 32.0) * 5.0 / 9.0 }}
fn main() { println!("100°C là {}°F", convert::c_to_f(100.0)); println!("212°F là {}°C", convert::f_to_c(212.0));}Kết quả:
100°C là 212°F212°F là 100°CCả hai hàm đều là pub, nên main (nằm bên ngoài module) gọi được chúng. Bỏ một chữ pub đi và trình biên dịch sẽ báo hàm đó là riêng tư.
2. Một hàm phụ trợ riêng tư
Phần tiêu đề “2. Một hàm phụ trợ riêng tư”Tạo module lồng school::grades với hàm công khai letter(score: u32) -> char trả về 'A' cho điểm từ 90 trở lên, 'B' từ 80 trở lên, và 'C' cho các trường hợp còn lại. Bên trong grades, viết thêm một hàm phụ trợ riêng tư is_at_least(score: u32, min: u32) -> bool mà letter sử dụng. Đưa school::grades vào phạm vi bằng use, rồi in xếp loại cho 95, 85 và 70.
Gợi ý: cả mod grades bên trong school và fn letter đều cần pub, nhưng is_at_least thì không nên có. Code bên trong cùng một module luôn gọi được các hàm riêng tư.
Xem lời giải
mod school { pub mod grades { pub fn letter(score: u32) -> char { if is_at_least(score, 90) { 'A' } else if is_at_least(score, 80) { 'B' } else { 'C' } }
// riêng tư: chỉ code bên trong `grades` mới gọi được hàm này fn is_at_least(score: u32, min: u32) -> bool { score >= min } }}
use school::grades;
fn main() { for score in [95, 85, 70] { println!("{score} -> {}", grades::letter(score)); }}Kết quả:
95 -> A85 -> B70 -> Cletter gọi được is_at_least vì chúng nằm trong cùng một module. Từ main, grades::is_at_least(95, 90) sẽ báo lỗi “function is_at_least is private” (hàm is_at_least là riêng tư). Đó chính là mục đích: module chỉ lộ ra một API công khai nhỏ và giữ các hàm phụ trợ cho riêng mình.
Tóm tắt
Phần tiêu đề “Tóm tắt”mod name { ... }gom code liên quan thành một module có tên.- Mọi thứ bên trong module mặc định là riêng tư — thêm
pubđể lộ nó ra bên ngoài module. useđưa một đường dẫn vào phạm vi để bạn không phải viết đầy đủ mỗi lần.- Trong dự án nhiều file,
mod name;(không có thân) nạp code của module đó từ file cùng tên. - Code bên trong module luôn dùng được các phần riêng tư của module đó, nên hãy giữ các hàm phụ trợ ở chế độ riêng tư và chỉ lộ ra những gì nơi gọi cần.
Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”Kiểm tra nhanh
1. Mặc định, các phần tử bên trong một mod là công khai hay riêng tư?
2. Làm sao bảo Rust nạp code của module từ math.rs thay vì viết thẳng bên trong?
Điểm: 0 / 2
Đọc thêm
Phần tiêu đề “Đọc thêm”- Managing Growing Projects with Packages, Crates, and Modules — The Rust Book (tiếng Anh): chương chính thức về package, crate và module, và cách chúng phối hợp với nhau.
- Defining Modules to Control Scope and Privacy — The Rust Book (tiếng Anh): một ví dụ hoàn chỉnh về cây module, kèm bảng tóm tắt quy tắc rất tiện.
- Separating Modules into Different Files — The Rust Book (tiếng Anh): cách bố trí nhiều file chi tiết hơn, gồm cả thư mục cho module lồng nhau.
- Modules — Rust by Example (tiếng Anh): ví dụ chạy được về phạm vi truy cập,
use,supervàself. - Visibility and privacy — The Rust Reference (tiếng Anh): các quy tắc chính xác, gồm cả
pub(crate)cho “chỉ công khai bên trong crate của tôi”.