Skip to main content

Crates

A crate is the smallest unit of compilation in Rust.

In simple terms:

  • A crate = one compiled output
  • It can produce:
    • an executable → binary crate
    • a reusable package → library crate

Modules organize code inside a crate
Crates organize code at the compilation level

Types of Crates

Rust has two main crate types:

Crate TypeOutputEntry File
Binary crateExecutable programmain.rs
Library crateReusable codelib.rs

Binary Crate (Executable)

A binary crate:

  • produces a runnable executable
  • must have a main() function
  • starts execution from main()

Example: Simple Binary Crate

my_app/
├── Cargo.toml
└── src/
└── main.rs

main.rs

fn main() {
println!("Hello from binary crate!");
}
  • main.rs is the crate root
  • main() is the program entry point
  • cargo run builds and executes this crate

Binary Crate with Modules

src/
├── main.rs
└── utils.rs

main.rs

mod utils;

fn main() {
utils::say_hi();
}

utils.rs

pub fn say_hi() {
println!("Hi from utils module");
}
  • main.rs is still the crate root
  • utils is a module inside the crate
  • pub allows access from main

Multiple Binary Crates in One Project

Rust allows multiple binaries in one package.

Structure

src/
├── main.rs // default binary
└── bin/
├── server.rs
└── client.rs

Running binaries

cargo run           # runs main.rs
cargo run --bin server
cargo run --bin client
  • Each file in src/bin/ is a separate binary crate
  • All share the same Cargo.toml
  • Useful for CLI tools, microservices, or test apps

Library Crate (Reusable Code)

A library crate:

  • does NOT have main()
  • exposes reusable functionality
  • is imported by other crates using use

Example: Simple Library Crate

my_lib/
├── Cargo.toml
└── src/
└── lib.rs

lib.rs

pub fn greet() {
println!("Hello from library crate");
}
  • lib.rs is the crate root
  • pub exposes the function to other crates
  • Built with cargo build

Using a Library Crate in a Binary Crate

Workspace-like usage (same project)

src/
├── main.rs
└── lib.rs

lib.rs

pub fn add(a: i32, b: i32) -> i32 {
a + b
}

main.rs

use my_project::add;

fn main() {
println!("{}", add(2, 3));
}
  • my_project = crate name from Cargo.toml
  • Binary crate automatically depends on the library crate
  • Common Rust pattern 👍

Library Crate with Modules

src/
├── lib.rs
├── math/
│ ├── mod.rs
│ └── add.rs
└── utils.rs

lib.rs

pub mod math;
pub mod utils;

math/mod.rs

pub mod add;

math/add.rs

pub fn add(a: i32, b: i32) -> i32 {
a + b
}

Usage in another crate

use my_lib::math::add::add;

Binary + Library Together (Very Common)

This is idiomatic Rust.

Structure

src/
├── main.rs // binary crate
└── lib.rs // library crate

Why do this?

  • Put logic in lib.rs
  • Keep main.rs thin
  • Improves testability and reuse

lib.rs

pub mod auth {
pub fn login() {
println!("User logged in");
}
}

main.rs

use my_app::auth::login;

fn main() {
login();
}

Crate Root Explained

Crate TypeCrate Root
Binarymain.rs
Librarylib.rs

The crate root:

  • defines the module tree
  • controls what is public
  • is where crate:: paths start

crate:: vs External Crates

Inside the same crate

crate::math::add::add();

External crate

use serde::Serialize;

Rust clearly separates:

  • internal modules → crate::
  • external crates → crate name

Cargo.toml and Crates

[package]
name = "my_app"
version = "0.1.0"
edition = "2021"
  • name = crate name used in use
  • Dependencies listed under [dependencies]
  • Cargo builds each crate independently

How Modules & Crates Fit Together

Think of it like this:

Workspace (optional)
└── Package (Cargo.toml)
├── Crate (binary or library)
│ └── Modules
│ └── Functions / structs

Key Differences: Crate vs Module

FeatureCrateModule
Compiled separately
Has crate root
Controls visibility
Can be published
File-basedYesYes

When to Use What

  • Modules → organize logic inside a crate
  • Library crate → reusable core logic
  • Binary crate → executable behavior

Rust strongly encourages:

“Write libraries, then write small binaries that use them.”