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.