📦

Crates and Source Files

Project Structure and Compilation Units

📚 Crate Structure Quick Reference

Binary Crate
Produces executable programs
src/main.rs - Entry point with main() function
Library Crate
Provides reusable functionality
src/lib.rs - Public API definitions
Module Tree
Hierarchical code organization
mod name; - Declares module from file
Cargo.toml
Project manifest and configuration
[package] - Metadata and build settings

Crate Structure Grammar

CRATE
BINARY_CRATE | LIBRARY_CRATE
BINARY_CRATE main.rs MODULE*
LIBRARY_CRATE lib.rs MODULE*

Project Structure Notation

PROJECT := MANIFEST SOURCE_ROOT MODULE_TREE? MANIFEST := "Cargo.toml" SOURCE_ROOT := "src/" (MAIN_RS | LIB_RS) MAIN_RS := "main.rs" # Binary crate LIB_RS := "lib.rs" # Library crate MODULE_TREE := MODULE+ MODULE := MOD_DECL | MOD_FILE | MOD_DIR

Actual Project Structure

my-project/ ├── Cargo.toml # Manifest ├── src/ # Source root │ ├── main.rs # Binary crate root │ ├── lib.rs # Library crate root │ └── modules/ # Module tree │ ├── parser.rs │ └── utils.rs └── target/ # Build output

Crates are the fundamental compilation units in Rust. They define the boundaries of namespaces and are the units of distribution. Every Rust program consists of one or more crates, and understanding their structure is essential for organizing and building Rust projects.

A crate corresponds to a single compilation unit and can produce either an executable binary or a library. The source code for a crate is organized in a tree of modules, starting from a crate root file.

🎮 Crate Structure Explorer

What is a Crate?

A crate is a collection of source files that are compiled together to produce either a library or an executable. Crates are the unit of compilation, meaning that rustc processes one crate at a time.

Crate Characteristics

  • Compilation Unit: Each crate is compiled independently
  • Namespace Root: Crates define the top-level namespace
  • Distribution Unit: Crates are published and shared as units
  • Dependency Graph: Crates can depend on other crates

Crate Name Resolution

Pattern extern crate NAME; use NAME::*;
Example extern crate serde; use serde::{Serialize, Deserialize};

Crate Types

Binary Crate

Produces an executable program with a main function. The crate root is typically src/main.rs.

Library Crate

Produces a library that can be used by other crates. The crate root is typically src/lib.rs.

Example Crate

Demonstrates usage of a library. Located in the examples/ directory.

Test Crate

Contains integration tests. Located in the tests/ directory.

Bench Crate

Contains benchmarks. Located in the benches/ directory.

Standard Project Structure

Cargo, Rust's build system and package manager, defines a standard project layout:

my-project/ ├── Cargo.toml # Project manifest ├── src/ # Source files │ ├── main.rs # Binary crate root │ ├── lib.rs # Library crate root │ └── bin/ # Additional binary targets │ └── another.rs ├── examples/ # Example programs │ └── example1.rs ├── tests/ # Integration tests │ └── integration.rs ├── benches/ # Benchmarks │ └── benchmark.rs └── target/ # Build artifacts (generated)
✅ Valid Project Structure
my-project/
├── Cargo.toml         # Contains [package] section
├── src/
│   └── lib.rs         # Has pub functions/structs
└── tests/
    └── integration.rs # Uses extern crate my_project;
❌ Invalid Project Structure
my-project/
├── Cargo.toml         # Missing [package] name
├── src/
│   ├── main.rs        # No main() function
│   └── lib.rs         # No pub items
└── lib/               # Wrong directory name for modules
    └── parser.rs

Crate Root Files

The crate root is the source file that the Rust compiler starts from and makes up the root module of your crate.

Binary Crate Root (main.rs)

// src/main.rs - Binary crate root
// This file defines the entry point for an executable

// Module declarations
mod utils;
mod config;

// External crate usage
use std::env;
use serde::Deserialize;

// Main function - entry point
fn main() {
    println!("Hello, world!");
    
    // Use modules from this crate
    let result = utils::process_data();
    
    // Handle command line arguments
    let args: Vec = env::args().collect();
    println!("Args: {:?}", args);
}

Library Crate Root (lib.rs)

// src/lib.rs - Library crate root
// This file defines the public API of a library

// Module declarations
pub mod parser;
pub mod processor;
mod internal_utils; // Private module

// Re-exports for convenience
pub use parser::{parse_input, ParseError};
pub use processor::ProcessResult;

// Public library functions
pub fn process_file(path: &str) -> Result {
    let content = std::fs::read_to_string(path)?;
    let parsed = parser::parse_input(&content)?;
    Ok(processor::process(parsed))
}

// Library-level documentation
//! # My Library
//! 
//! This library provides functionality for processing data files.
//! 
//! ## Examples
//! 
//! ```rust
//! use my_library::process_file;
//! 
//! let result = process_file("data.txt")?;
//! println!("Processed: {:?}", result);
//! ```

Module Tree and File Organization

Rust uses a module tree to organize code within a crate. Modules can be defined inline or in separate files.

Inline Modules

// Inline module definition
mod network {
    pub mod client {
        pub fn connect() -> Result {
            // Implementation
        }
    }
    
    pub mod server {
        pub fn start(port: u16) -> Result<(), Error> {
            // Implementation
        }
    }
}

File-based Modules

Modules can be organized in separate files:

src/ ├── lib.rs # Crate root ├── network.rs # Module file └── network/ # Module directory ├── mod.rs # Module root ├── client.rs # Submodule └── server.rs # Submodule
// src/lib.rs
mod network;        // Declares the network module

pub use network::client::connect;

// src/network.rs or src/network/mod.rs
pub mod client;     // Declares client submodule
pub mod server;     // Declares server submodule

// src/network/client.rs
pub fn connect() -> Result {
    // Implementation
}

Cargo.toml Configuration

The Cargo.toml file defines crate metadata and build configuration:

[package]
name = "my-project"
version = "0.1.0"
edition = "2021"
authors = ["Your Name "]
description = "A sample Rust project"
license = "MIT"
repository = "https://github.com/user/my-project"

# Define library target
[lib]
name = "my_project"
path = "src/lib.rs"

# Define binary targets
[[bin]]
name = "main-app"
path = "src/main.rs"

[[bin]]
name = "helper-tool"
path = "src/bin/helper.rs"

# Dependencies
[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }

# Development dependencies (for tests and examples)
[dev-dependencies]
tempfile = "3.0"
criterion = "0.3"

# Build dependencies
[build-dependencies]
cc = "1.0"

Multiple Binary Targets

A single crate can contain multiple binary targets:

src/ ├── lib.rs # Library code ├── main.rs # Primary binary └── bin/ # Additional binaries ├── server.rs # Server binary └── client.rs # Client binary
# Build and run different binaries
cargo build                    # Build all targets
cargo run                      # Run the primary binary (main.rs)
cargo run --bin server         # Run the server binary
cargo run --bin client         # Run the client binary

Workspace Organization

For larger projects, you can organize multiple related crates in a workspace:

🏗️ Workspace Structure Example

my-workspace/ ├── Cargo.toml # Workspace manifest ├── Cargo.lock # Dependency lock file ├── common/ # Shared library crate │ ├── Cargo.toml │ └── src/ │ ├── lib.rs # Library root │ ├── types.rs # Shared types │ └── utils.rs # Shared utilities ├── server/ # Server application crate │ ├── Cargo.toml │ └── src/ │ ├── main.rs # Server entry point │ ├── config.rs # Configuration │ └── handlers.rs # Request handlers ├── client/ # Client application crate │ ├── Cargo.toml │ └── src/ │ ├── main.rs # Client entry point │ ├── api.rs # API client │ └── ui.rs # User interface └── target/ # Shared build artifacts

Workspace Benefits

Shared Dependencies: All crates use the same versions
Unified Build: Build all crates with one command
Cross-crate Development: Easy development across related crates
Consistent Tooling: Shared lint, test, and format settings
# Workspace Cargo.toml
[workspace]
members = [
    "common",
    "server", 
    "client"
]

# Shared dependencies across workspace
[workspace.dependencies]
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
anyhow = "1.0"

# Shared metadata
[workspace.package]
authors = ["Your Team "]
edition = "2021"
license = "MIT"
repository = "https://github.com/org/my-workspace"

Compilation Process

Understanding how Rust compiles crates helps in organizing code effectively:

Compilation Steps

  1. Dependency Resolution: Cargo resolves and downloads dependencies
  2. Crate Compilation: Each crate is compiled independently
  3. Module Resolution: The compiler builds the module tree from the crate root
  4. Code Generation: Rust code is compiled to machine code or intermediate representation
  5. Linking: Object files and libraries are linked to create the final executable

Incremental Compilation

Rust supports incremental compilation, rebuilding only what's necessary:

# Enable incremental compilation (default in development)
export CARGO_INCREMENTAL=1

# Check compilation times
cargo build --timings

# Clean build artifacts
cargo clean

🦀 Rust-Specific Crate Quirks

🔗 Edition Boundaries

EDITION := "2015" | "2018" | "2021" | "2024" CRATE_EDITION := EDITION # Per-crate setting WORKSPACE_EDITION := EDITION # Workspace default
# Different crates can use different editions
[package]
edition = "2021"  # This crate uses 2021

[dependencies]
old-crate = "1.0"  # Might use edition 2018

🔗 Crate Name vs Package Name

PACKAGE_NAME := kebab-case-name CRATE_NAME := snake_case_name EXTERN_CRATE := CRATE_NAME | PACKAGE_NAME
[package]
name = "my-awesome-lib"  # Package name (kebab-case)

[lib]
name = "my_awesome_lib"  # Crate name (snake_case)

Best Practices

Project Organization

  • Small Crates: Prefer many small, focused crates over large monolithic ones
  • Clear APIs: Design public interfaces carefully in library crates
  • Module Hierarchy: Use nested modules to organize related functionality
  • Integration Tests: Place integration tests in the tests/ directory

File Naming

  • Use snake_case for file and directory names
  • Match module names to file names
  • Use mod.rs for module directories
  • Place related modules in subdirectories

🤖 For AI Coding Agents

Crate Structure Generation Instructions

When creating Rust projects, follow these machine-readable specifications:

{
  "crate_types": {
    "binary": {
      "entry_point": "src/main.rs",
      "requires": ["main() function"],
      "produces": "executable"
    },
    "library": {
      "entry_point": "src/lib.rs", 
      "requires": ["pub items"],
      "produces": "rlib"
    }
  },
  "project_structure": {
    "required": ["Cargo.toml", "src/"],
    "optional": ["tests/", "examples/", "benches/"],
    "generated": ["target/", "Cargo.lock"]
  },
  "naming_conventions": {
    "package_name": "kebab-case",
    "crate_name": "snake_case",
    "file_names": "snake_case.rs",
    "module_names": "snake_case"
  }
}

Cargo.toml Template Generation

Template [package] name = "NAME" version = "VERSION" Complete manifest with metadata
{
  "manifest_sections": {
    "required": ["package"],
    "common": ["dependencies", "dev-dependencies"],
    "targets": ["lib", "bin", "example", "test", "bench"]
  },
  "validation_rules": {
    "package_name": "^[a-zA-Z][a-zA-Z0-9_-]*$",
    "version": "semver format",
    "edition": ["2015", "2018", "2021", "2024"]
  }
}
← Rust Editions Conditional Compilation → ← Back to Reference