Danh sách bài viết

Bài 17: Cargo Là Gì? Vai Trò Trong Project Rust

Bài 17 của series Rust Cơ Bản — Cargo là build system + package manager + test runner + doc generator + workspace manager + benchmark runner all-in-one của Rust, viết bằng Rust và đi kèm rustup theo mặc định nên mọi dev Rust trên thế giới đều có Cargo identical. Bài này không lặp lại tổng quan ecosystem ở Bài 7 mà đi sâu vào định nghĩa, 4 vai trò chính, so sánh với npm / pip / CMake / Maven và bản đồ subcommand quan trọng sẽ học chi tiết ở các bài tiếp theo trong Nhóm 3.

09/06/2026
10 phút đọc
1 lượt xem
1

Mục Tiêu Bài Học

Sau bài học, bạn sẽ:

  • Định nghĩa được Cargo là gì và 6 vai trò bao trùm trong vòng đời một project Rust (build, package, test, doc, workspace, bench).
  • Hiểu vì sao Cargo đi kèm rustup mặc định và không bao giờ phải cài rời như các language tooling khác.
  • Phân biệt rõ 4 vai trò chính: build system (gọi rustc + link + incremental), package manager (resolve dep + lock file), test runner (cargo test built-in), doc generator (cargo doc + docs.rs).
  • So sánh được Cargo với npm (Node), pip (Python), CMake (C/C++), Maven/Gradle (Java) để hiểu vị thế "all-in-one" là điểm khác biệt cốt lõi.
  • Có bản đồ subcommand quan trọng (build, run, check, test, doc, add, install, fmt, clippy, expand, bench…) để biết bài tiếp theo nào trong Nhóm 3 sẽ học sâu cái gì.
  • Biết cách dùng cargo --list để khám phá subcommand built-in và third-party đã cài qua cargo install.

Bài này KHÔNG lặp lại "tổng quan ecosystem" đã có ở Bài 7: Hệ Sinh Thái Rust. Trọng tâm ở đây là chính bản thân Cargo — định nghĩa, vai trò, lệnh.

2

Định Nghĩa Cargo

Cargo là CLI tool chính thức của Rust, gom 6 vai trò trong một binary duy nhất:

  • Build system — gọi rustc đúng flag, link binary, quản lý incremental compilation.
  • Package manager — khai báo và resolve dependency, fetch từ crates.io / git / path.
  • Test runnercargo test chạy mọi hàm có thuộc tính #[test], kèm integration test và doc test.
  • Documentation generatorcargo doc dùng rustdoc build HTML từ comment ///.
  • Workspace manager — quản lý project nhiều crate chung target/ và Cargo.lock.
  • Benchmark runnercargo bench chạy micro-benchmark (built-in cần nightly, hoặc dùng crate criterion trên stable).

Cargo do chính Rust team duy trì, được viết bằng Rust và mã nguồn ở repo rust-lang/cargo. Phiên bản đi cùng rustc theo cùng release train 6 tuần.

Lịch sử ngắn: Cargo ra mắt cùng Rust 0.9 vào tháng 1/2014 và trở thành công cụ build mặc định kể từ Rust 1.0 (5/2015). Tài liệu chính thức là The Cargo Book (thường được gọi là cargo-the-book) tại doc.rust-lang.org/cargo/.

Vì sao Cargo đi kèm rustup theo mặc định: khi cài Rust qua rustup-init, bạn nhận được bộ ba rustc (compiler), cargo (tool quản lý project), rustup (toolchain manager) cùng lúc. Không có khái niệm "cài Cargo riêng" — Cargo luôn được phân phối cùng compiler. Lý do thiết kế: đảm bảo mọi developer Rust trên thế giới đều có Cargo identical về behavior, version khớp với rustc đang dùng. Tránh tình trạng phân mảnh "team A dùng tool build này, team B dùng tool kia" như hệ JavaScript với npm / yarn / pnpm / bun, hay hệ C/C++ với make / cmake / meson / bazel / xmake.

Kiểm tra version Cargo:

cargo --version
cargo --help

Output cargo --version dạng cargo 1.83.0 (xxxxxxxx 2026-xx-xx) — version đi cùng rustc cùng release.

3

Vai Trò 1: Build System

Vai trò đầu tiên và cơ bản nhất của Cargo là build system. Bạn không gọi trực tiếp rustc như khi học hello world ở giai đoạn rất sớm — bạn gọi cargo build và Cargo tự lo phần còn lại.

Cargo chịu trách nhiệm:

  • Gọi rustc với flag đúng — đọc Cargo.toml để biết edition (2015 / 2018 / 2021 / 2024), profile (dev / release), feature flag, target triple, sau đó dịch sang chuỗi argument truyền vào rustc. Bạn không phải nhớ -C opt-level=3 -C lto -C codegen-units=1 --edition 2024 ....
  • Link binary — sau khi compile từng crate ra object/rlib, Cargo gọi linker hệ thống (ld trên Linux, link.exe trên Windows MSVC, ld64 trên macOS) để tạo executable hoặc library cuối cùng.
  • Incremental compilation qua target/ — thư mục target/ lưu kết quả compile của từng crate (target/debug/, target/release/, fingerprint, dep-info, build-script output). Lần build sau, Cargo so sánh fingerprint để biết file nào đổi, chỉ rebuild đúng phần cần thiết. Lần đầu build thường lâu (mọi dependency phải compile), từ lần thứ 2 trở đi rất nhanh.
  • Theo dõi dependency graph — biết crate nào phụ thuộc crate nào, build theo topo order, parallel hoá compile khi không có conflict (mặc định dùng số CPU core).

Khi bạn sửa 1 dòng trong main.rs của crate gốc, Cargo chỉ recompile crate gốc — không recompile lại 200 dependency. Khi bạn sửa Cargo.toml đổi version một dependency, Cargo recompile crate đó cùng các crate downstream phụ thuộc nó. Quy tắc đúng giúp dev loop ngắn.

4

Vai Trò 2: Package Manager

Vai trò thứ hai: package manager. Trong Rust, một gói (package) được gọi là một crate. Cargo lo việc khai báo, fetch, resolve và lock dependency.

  • Khai báo dependency trong Cargo.toml — section [dependencies], [dev-dependencies] (chỉ cho test), [build-dependencies] (cho build script). Mỗi dòng dạng tokio = "1" hoặc axum = { version = "0.7", features = ["macros"] }.
  • Fetch nguồn linh hoạt — mặc định từ crates.io. Cũng hỗ trợ git ({ git = "https://github.com/...", branch = "main" }), path local ({ path = "../core" }), hoặc registry tự host. Bạn có thể trộn cả 3 trong cùng project.
  • Resolve version theo SemVer"1" nghĩa là >=1.0.0, <2.0.0. Cargo dùng SAT solver chọn ra một version cho mỗi crate sao cho thoả mãn toàn bộ constraint trong cây dependency.
  • Lock file để reproducible — sau khi resolve, version cụ thể được ghi vào Cargo.lock. Lần build sau, nếu file lock có sẵn, Cargo dùng đúng version trong lock — build cho ra binary giống hệt nhau dù 6 tháng sau dependency có release version mới.
  • Lệnh quản lý dependencycargo add <crate> thêm, cargo remove <crate> xoá, cargo update regenerate Cargo.lock theo phiên bản mới nhất thoả constraint, cargo tree in dependency graph dạng cây.

Khác biệt với npm: Cargo không có node_modules nằm trong project. Dependency tải về một lần vào ~/.cargo/registry/, dùng chung cho mọi project — tiết kiệm disk và bandwidth. Khi build, Cargo copy/link vào target/ riêng của project.

Khác biệt với pip: Cargo có lock file mặc định, không cần thêm tool như pip-tools hay poetry. Chính Cargo lo workspace multi-crate, không cần plugin.

5

Vai Trò 3: Test Runner

Vai trò thứ ba: test runner built-in. Không cần cài Jest, Mocha, Pytest hay JUnit riêng — Cargo có sẵn test framework.

  • cargo test chạy mọi hàm có #[test] — bất kỳ hàm nào trong codebase được đánh dấu #[test] đều được phát hiện và chạy. Test thường viết trong cùng file với code (module #[cfg(test)] mod tests { ... }) hoặc thư mục riêng.
  • Unit test — bên trong cùng module, có thể truy cập item private. Idiom Rust khuyến khích viết test ngay cạnh code.
  • Integration test trong tests/ — mỗi file .rs trong thư mục tests/ là một test crate độc lập, chỉ thấy public API của crate. Mô phỏng cách "user thật" sử dụng library.
  • Doc test trong /// — code block trong comment /// được compile và chạy như test. Đảm bảo ví dụ trong tài liệu luôn cập nhật, không bao giờ lỗi thời.
  • Parallel execution — test chạy song song trên nhiều thread mặc định, dùng --test-threads=1 để chạy serial khi có shared state.
  • cargo bench — chạy hàm có thuộc tính #[bench]. Built-in cần nightly; trên stable dùng crate criterion (cài qua [dev-dependencies]) cho output thống kê chi tiết.

Một lệnh cargo test chạy hết cả 3 loại test (unit + integration + doc). Không cần config nào — convention over configuration triệt để.

6

Vai Trò 4: Documentation Generator

Vai trò thứ tư: documentation generator. Lệnh cargo doc chạy rustdoc để build HTML doc từ comment /// trong source code, đưa vào target/doc/. Thêm --open để mở trình duyệt ngay sau khi build.

  • Comment /// (outer doc) cho item bên dưới, //! (inner doc) cho module / crate chứa nó. Cả hai đều viết bằng Markdown — heading, list, code block, link đều render đẹp.
  • Doc bao gồm cả dependency — chạy cargo doc --open bạn thấy doc cho crate của mình kèm doc cho mọi dependency trong cây. Đọc offline, không cần internet, version khớp với Cargo.lock.
  • Mỗi crate publish auto build trên docs.rs — sau khi cargo publish đẩy crate lên crates.io, docs.rs tự chạy cargo doc trong sandbox và host kết quả ở https://docs.rs/<ten-crate>/<version>. Bạn không phải tự host doc. Ví dụ mở https://docs.rs/tokio là thấy API doc đầy đủ của Tokio version mới nhất; mở https://docs.rs/tokio/1.40.0 để xem đúng version bạn dùng.
  • Doc test — code block trong /// được cargo test chạy như test. Documentation và test gắn liền nhau.

So với hệ Node phải dùng JSDoc / TypeDoc rời, hay Python phải dùng Sphinx config bằng tay, Cargo + rustdoc + docs.rs là một pipeline auto từ "viết comment" → "publish" → "đọc online" mà không cần cấu hình thêm.

7

So Với Tool Cộng Đồng Khác

Để hiểu rõ vị thế "all-in-one" của Cargo, so với toolchain các ngôn ngữ khác:

  • npm (Node.js) — chỉ lo dependency và run script. Cần ESLint cho lint, Prettier cho format, Jest/Vitest cho test, tsc cho typecheck, esbuild/tsup/webpack cho bundle, TypeDoc cho doc. Mỗi tool config riêng, version riêng, đôi khi conflict version giữa các tool. Cargo gom hết.
  • pip (Python) — package manager đơn giản, thiếu workspace (multi-package trong cùng repo), lock file chỉ có một phần (qua pip freeze hoặc thêm pip-tools/poetry/uv). Test riêng (pytest), lint riêng (ruff/flake8), doc riêng (sphinx). Cargo có sẵn tất cả.
  • CMake (C/C++) — chỉ build và link. Dependency management hoàn toàn riêng: vcpkg, conan, hunter, hoặc thủ công git submodule. Test phải config thêm CTest. Không có doc generator built-in. C/C++ ecosystem nổi tiếng vì sự rời rạc — Cargo là một bước nhảy về integration.
  • Maven / Gradle (Java) — gần Cargo nhất về phạm vi (build + dependency + test + plugin doc). Khác biệt: XML/Groovy/Kotlin DSL phức tạp hơn TOML của Cargo nhiều lần. Build cycle phức tạp (phase, goal, lifecycle). Cargo đơn giản hơn về mô hình, nhanh hơn về incremental build, và không cần JVM warmup.

Một số ngôn ngữ mới (Go, Deno, Bun) học theo Cargo về tinh thần "tooling tích hợp": go build, go test, go mod, go fmt, go doc đi cùng compiler — minh chứng cho hướng đi này được công nhận trong industry.

8

Subcommand Quan Trọng Overview

Đây là bản đồ subcommand bạn sẽ chạm thường nhất. Mỗi cái sẽ có bài riêng học sâu trong Nhóm 3 hoặc các nhóm sau:

  • cargo new <name> — tạo project mới với cấu trúc chuẩn (Bài 18).
  • cargo init — biến thư mục hiện tại thành Cargo project (Bài 18).
  • cargo build — compile project, output ra target/debug/ hoặc target/release/ (Bài 21).
  • cargo run — build và chạy binary luôn (Bài 21).
  • cargo check — typecheck nhanh, không gen binary — vòng feedback ngắn nhất khi đang viết code (Bài 21).
  • cargo test — chạy unit + integration + doc test.
  • cargo doc — build HTML doc bằng rustdoc, --open để mở trình duyệt.
  • cargo add <crate> — thêm dependency vào Cargo.toml tự động (built-in từ Cargo 1.62).
  • cargo update — regenerate Cargo.lock theo phiên bản mới nhất thoả constraint (Bài 23).
  • cargo publish — đẩy crate lên crates.io.
  • cargo install <crate> — cài binary crate global vào ~/.cargo/bin/ (khác cargo add chỉ thêm vào project hiện tại).
  • cargo clean — xoá target/, giải phóng disk khi cần.
  • cargo fmt — format code theo rustfmt (component rustfmt).
  • cargo clippy — chạy linter với hơn 700 rule (component clippy).
  • cargo expand — mở rộng macro để xem code thực sự sinh ra (cài qua cargo install cargo-expand).
  • cargo bench — chạy benchmark.

Workflow tối thiểu để tạo và chạy một app:

cargo new my-app
cd my-app
cargo run

3 lệnh trên đủ để có một binary "Hello, world!" chạy được. Không cần tạo file bằng tay, không cần config build tool. Học chi tiết flow Hello World ở Bài 24 trong Nhóm 3.

9

cargo --list Khám Phá Subcommand

Lệnh cargo --list in ra toàn bộ subcommand mà Cargo nhận diện được trên máy bạn — bao gồm cả built-in lẫn third-party.

cargo --list

Output chia 2 nhóm:

  • Built-in commands — đến từ binary cargo chính: build, check, run, test, doc, add, remove, update, publish, install, clean, fetch, tree, vendor, search, yank, login...
  • External subcommands — bất kỳ executable nào trong $PATH có tên dạng cargo-<ten> sẽ được Cargo tự nhận diện. Gọi cargo foo sẽ exec file cargo-foo.

Cơ chế "external subcommand" cho phép cộng đồng mở rộng Cargo mà không cần fork. Cài bằng cargo install <crate>, binary tự rơi vào ~/.cargo/bin/ (đã có sẵn trong PATH sau khi rustup setup), và xuất hiện ngay trong cargo --list.

Vài subcommand third-party quen thuộc:

  • cargo-edit — bộ subcommand add, rm, upgrade (trước Cargo 1.62 không có sẵn). Hiện add/remove đã built-in, upgrade vẫn từ crate này.
  • cargo-watch — tự động chạy lại lệnh khi file thay đổi, rất hữu ích cho dev loop (cargo watch -x run kiểu nodemon của Node).
  • cargo-machete — tìm dependency không dùng đến trong Cargo.toml, gợi ý xoá để giảm build time.
  • cargo-expand, cargo-audit, cargo-deny, cargo-nextest, cargo-flamegraph, cargo-llvm-cov… mỗi cái giải một bài toán cụ thể.

Cài thử một subcommand third-party:

cargo install cargo-watch

Sau khi cài xong, cargo --list sẽ có thêm dòng watch, và bạn dùng cargo watch -x check hay cargo watch -x run như subcommand chính thức. Đây là điểm extend rất sạch — không có cơ chế plugin phức tạp, chỉ là một binary trong PATH với tên đúng convention.

10

Tổng Kết

  • Cargo là CLI all-in-one của Rust với 6 vai trò: build system, package manager, test runner, doc generator, workspace manager, benchmark runner.
  • Cargo viết bằng Rust, đi cùng rustup mặc định, version khớp với rustc theo cùng release train 6 tuần — không cần cài rời.
  • Build system: gọi rustc đúng flag, link binary, incremental compilation qua target/, theo dõi dependency graph để rebuild đúng phần cần thiết.
  • Package manager: khai báo trong Cargo.toml, fetch từ crates.io/git/path, resolve theo SemVer, lock vào Cargo.lock.
  • Test runner built-in: cargo test chạy hết unit + integration + doc test trong một lệnh.
  • Doc generator: cargo doc dùng rustdoc, mỗi crate publish auto build trên docs.rs theo version.
  • So với npm / pip / CMake / Maven: Cargo là tích hợp nhất, đơn giản nhất về mô hình config (TOML).
  • Subcommand quan trọng cần nhớ: new, init, build, run, check, test, doc, add, update, install, fmt, clippy.
  • cargo --list in built-in + third-party subcommand; extend bằng cách cài binary tên cargo-<ten> qua cargo install.
  • Cargo ra mắt cùng Rust 0.9 (1/2014), mặc định từ Rust 1.0 (5/2015). Tài liệu: The Cargo Book tại doc.rust-lang.org/cargo/.
11

Bài Tập Củng Cố

Tự trả lời, đáp án ở cuối:

  1. Cargo gom 6 vai trò trong cùng một CLI. Liệt kê 6 vai trò đó và một subcommand đại diện cho mỗi vai trò.
  2. Một developer mới hỏi: "Sau khi cài rustup xong, có phải cài Cargo riêng không?". Trả lời và giải thích lý do thiết kế.
  3. So sánh ngắn gọn Cargo với npm (Node.js): điểm giống và điểm khác cốt lõi.
  4. Bạn cần một tool tự động chạy lại cargo run mỗi khi file .rs thay đổi (giống nodemon). Có sẵn trong Cargo built-in không? Nếu không, làm sao có?
  5. Mở terminal và chạy cargo --list. Phân biệt được dòng nào là built-in command, dòng nào là third-party đã cài qua cargo install. Cơ chế nào cho phép Cargo "thấy" được third-party subcommand?
Đáp án
  1. (1) Build system — cargo build. (2) Package manager — cargo add / cargo update. (3) Test runner — cargo test. (4) Documentation generator — cargo doc. (5) Workspace manager — quản lý multi-crate qua [workspace] trong Cargo.toml. (6) Benchmark runner — cargo bench.
  2. Không cần cài Cargo riêng. Khi cài Rust qua rustup-init sẽ tự kèm cả rustc, cargorustup. Lý do thiết kế: đảm bảo mọi dev Rust có Cargo identical về behavior và version khớp với rustc đang dùng, tránh phân mảnh tooling như hệ npm/yarn/pnpm/bun của Node.
  3. Giống: cả hai đều là CLI tool, lo dependency declared trong file manifest (Cargo.toml vs package.json), có lock file (Cargo.lock vs package-lock.json), publish lên registry chính thức (crates.io vs npmjs.com). Khác cốt lõi: Cargo gom build + test + doc + lint (qua clippy) + format (qua rustfmt) vào cùng workflow chính thức; npm chỉ lo dependency và run script — phần còn lại phải dùng ESLint + Prettier + Jest + tsc + TypeDoc + bundler rời. Cấu hình Cargo (TOML) đơn giản hơn rất nhiều so với hệ package.json + tsconfig + .eslintrc + jest.config + prettier.config.
  4. Không có sẵn trong Cargo built-in. Cách có: cài crate cargo-watch bằng cargo install cargo-watch, sau đó dùng cargo watch -x run. Đây là third-party subcommand — binary cargo-watch nằm trong ~/.cargo/bin/ (đã có trong PATH), được Cargo tự nhận diện qua convention tên file.
  5. Output cargo --list thường nhóm "Installed Commands" trước, bao gồm cả built-in lẫn external. Dòng built-in là các lệnh đến trực tiếp từ binary cargo (build, check, run, test, doc, add, remove, update, publish, install, clean, fetch, tree...). Dòng third-party là các lệnh bạn đã cài qua cargo install (watch, expand, audit, deny, machete, nextest...). Cơ chế: bất kỳ executable trong $PATH có tên dạng cargo-<ten> sẽ được Cargo tự nhận; gọi cargo foo tương đương exec file cargo-foo.
12

Bài Tiếp Theo

Bài 18: cargo new vs cargo init — Tạo Project Mới — phân biệt cargo new my-app tạo thư mục mới và cargo init áp dụng vào thư mục hiện tại, các flag quan trọng --lib, --bin, --vcs, --edition 2024, --name, và khi nào dùng lệnh nào.