Packages và Crates

Phần đầu tiên của hệ thống module mà chúng ta sẽ đề cập đến là packages và crates.

Một crate là đơn vị code nhỏ nhất mà trình biên dịch Rust xem xét tại một thời điểm. Ngay cả khi bạn chạy rustc thay vì cargo và truyền vào một tệp mã nguồn duy nhất (như chúng ta đã làm trong "Viết và Chạy Chương Trình Rust" ở Chương 1), trình biên dịch xem tệp đó là một crate. Crates có thể chứa modules, và các modules có thể được định nghĩa trong các tệp khác được biên dịch cùng với crate, như chúng ta sẽ thấy trong các phần tiếp theo.

Một crate có thể có một trong hai hình thức: binary crate hoặc library crate. Binary crates là các chương trình bạn có thể biên dịch thành một tệp thực thi để chạy, như một chương trình dòng lệnh hoặc một máy chủ. Mỗi crate này phải có một hàm gọi là main để xác định điều gì sẽ xảy ra khi chương trình thực thi chạy. Tất cả các crate chúng ta đã tạo cho đến nay đều là binary crates.

Library crates không có hàm main, và chúng không được biên dịch thành tệp thực thi. Thay vào đó, chúng định nghĩa các chức năng được thiết kế để chia sẻ giữa nhiều dự án. Ví dụ, crate rand mà chúng ta đã sử dụng trong Chương 2 cung cấp chức năng tạo số ngẫu nhiên. Hầu hết thời gian khi các lập trình viên Rust nói “crate,“ họ thường muốn nói đến library crate, và họ sử dụng "crate" thay thế cho khái niệm lập trình chung là “library.“

Crate root là một tệp mã nguồn mà trình biên dịch Rust bắt đầu từ đó và tạo thành module gốc của crate của bạn (chúng ta sẽ giải thích kỹ về modules trong "Định nghĩa Modules để Kiểm soát Phạm vi và Quyền riêng tư").

Một package là một gói gồm một hoặc nhiều crates cung cấp một tập hợp chức năng. Một package chứa một tệp Cargo.toml mô tả cách xây dựng các crates đó. Cargo thực chất là một package chứa binary crate cho công cụ dòng lệnh mà bạn đã sử dụng để xây dựng mã của mình. Package Cargo cũng chứa một library crate mà binary crate phụ thuộc vào. Các dự án khác có thể phụ thuộc vào library crate Cargo để sử dụng logic tương tự như công cụ dòng lệnh Cargo sử dụng.

Một package có thể chứa nhiều binary crates tùy thích, nhưng nhiều nhất chỉ có một library crate. Một package phải chứa ít nhất một crate, dù đó là library hay binary crate.

Hãy đi qua những gì xảy ra khi chúng ta tạo một package. Đầu tiên, chúng ta nhập lệnh cargo new my-project:

$ cargo new my-project
     Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs

Sau khi chúng ta chạy cargo new my-project, chúng ta sử dụng ls để xem những gì Cargo tạo ra. Trong thư mục dự án, có một tệp Cargo.toml, tạo ra một package. Có một thư mục src chứa main.rs. Mở Cargo.toml trong trình soạn thảo của bạn và lưu ý rằng không có đề cập đến src/main.rs. Cargo tuân theo quy ước rằng src/main.rs là crate root của một binary crate có cùng tên với package. Tương tự, Cargo biết rằng nếu thư mục package chứa src/lib.rs, thì package chứa một library crate có cùng tên với package, và src/lib.rs là crate root. Cargo truyền các tệp crate root cho rustc để xây dựng thư viện hoặc chương trình.

Ở đây, chúng ta có một package chỉ chứa src/main.rs, nghĩa là nó chỉ chứa một binary crate có tên là my-project. Nếu một package chứa src/main.rssrc/lib.rs, nó có hai crates: một binary và một library, cả hai đều có cùng tên với package. Một package có thể có nhiều binary crates bằng cách đặt các tệp trong thư mục src/bin: mỗi tệp sẽ là một binary crate riêng biệt.