Cargo, Crates & the Module Workflow
Cargo is Rust's official build system and package manager — it creates projects, compiles your code, runs tests, manages dependencies, and ties together the crates and modules that make up every Rust program.
Learn Cargo, Crates & the Module Workflow in our free Rust course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a…
Part of the free Rust course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll learn the everyday Cargo commands, what lives in Cargo.toml , how crates differ from modules, how to add dependencies, and how cargo fmt and cargo clippy keep your code clean.
What You'll Learn in This Lesson
1️⃣ Creating & Running a Project
A Cargo package starts with one command. cargo new scaffolds a folder with a Cargo.toml manifest, a src/main.rs entry point, and a git repo with /target already ignored. From there you compile and run with a single command. These are commands you type in your terminal, not Rust code:
The src/main.rs that Cargo runs always starts at the main function. Here is a slightly fuller version of that file — this is the real Rust that cargo run would build and execute:
2️⃣ Cargo.toml, Crates & Dependencies
The Cargo.toml manifest describes your package: its name, version, Rust edition, and the external crates it depends on. Each entry under [dependencies] is a crate downloaded from crates.io , Rust's package registry. A typical manifest looks like this:
You rarely edit dependencies by hand — cargo add rand inserts the line for you and resolves a compatible version. Cargo then records the exact version in Cargo.lock so builds are reproducible. A crate is the whole compilation unit; the standard library is itself a crate ( std ) that's always available. Inside a single crate, you organize code into modules :
Items are private by default; pub exposes them. You reach into a module with the :: path separator — the same syntax you use to reach into an external crate (e.g. rand::random ).
3️⃣ The use Keyword & Keeping Code Clean
Typing full paths everywhere gets noisy. The use keyword brings a path into scope so you can call it by its short name. You can import several items at once with braces:
Once your code grows, two commands keep it tidy. Run them in your terminal:
Your turn. Fill in the blanks marked ___ , then run it.
Organize a small conversion into its own module, import it with use , and loop over an array. Run it with cargo run and check the output.
📋 Quick Reference — Cargo
Practice quiz
What is the difference between a crate and a module?
- They are the same thing
- A module is the compilation unit; a crate is inside it
- A crate is the compilation unit; a module organizes code inside a crate
- Modules are only for tests
Answer: A crate is the compilation unit; a module organizes code inside a crate. A crate is what Cargo builds; modules are namespaces that organize code within a crate.
Which command compiles AND runs your binary in one step?
- cargo run
- cargo check
- cargo build
- cargo new
Answer: cargo run. cargo run compiles the crate (and dependencies) then executes the resulting binary.
What does cargo check do that cargo build does not?
- Runs the tests
- Publishes the crate
- Formats the code
- Type-checks quickly without producing a binary
Answer: Type-checks quickly without producing a binary. cargo check skips code generation, so it type-checks much faster than a full build.
By default, items in a module are visible from outside it...
- Always
- Never — they are private unless marked pub
- Only inside tests
- Only if named in UPPERCASE
Answer: Never — they are private unless marked pub. Items are private by default; pub exposes them outside their module.
Which path separator reaches into a module or external crate?
- ::
- .
- ->
- /
Answer: ::. Rust uses the :: path separator, e.g. geometry::area or rand::random.
What does this print? mod math { pub fn double(n: i32) -> i32 { n * 2 } } use math::double; fn main() { println!("{}", double(double(5))); }
- 10
- 25
- 20
- It does not compile
Answer: 20. double(5) is 10, and double(10) is 20.
What does the use keyword do?
- Declares a new module
- Brings a path into scope so you can call it by its short name
- Downloads a crate
- Runs the tests
Answer: Brings a path into scope so you can call it by its short name. use imports a path into scope so you avoid typing full paths everywhere.
Where does Cargo declare a package's dependencies?
- src/main.rs
- Cargo.lock
- target/debug
Dependencies are listed under [dependencies] in the Cargo.toml manifest.
Should you commit the target/ directory to version control?
- Yes, always
- No — it is generated build output and is git-ignored by default
- Only the release build
- Only Cargo.lock inside it
Answer: No — it is generated build output and is git-ignored by default. target/ holds reproducible generated artifacts; cargo new git-ignores it for you.
What do cargo fmt and cargo clippy do, respectively?
- Run tests and build
- Publish and download
- Format code and lint for mistakes/idioms
- Both format code
Answer: Format code and lint for mistakes/idioms. cargo fmt formats to the canonical style; cargo clippy lints for bugs and non-idiomatic code.