Skip to content

14 - Anatomy of a Rust OSS Repo

What this session is

About 45 minutes. Walk through the file layout of a typical Rust open-source project, file by file.

Typical Rust project layout

.
├── README.md
├── LICENSE
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
├── Cargo.toml
├── Cargo.lock
├── .gitignore
├── .github/
│   ├── workflows/
│   ├── ISSUE_TEMPLATE/
│   └── PULL_REQUEST_TEMPLATE.md
├── src/
│   ├── lib.rs            (library entry; or main.rs for binary)
│   ├── module1.rs
│   ├── module2/
│   │   ├── mod.rs
│   │   └── inner.rs
│   └── bin/              (additional binaries, optional)
│       └── tool.rs
├── tests/
│   └── integration_test.rs
├── benches/
│   └── benchmark.rs      (criterion)
├── examples/
│   └── basic.rs
└── target/               (build output, gitignored)

Not every project has every piece. The roles are consistent.

What each piece is for

Root-level files

  • README.md - homepage. One-line description; install; smallest example.
  • LICENSE - usually MIT OR Apache-2.0 (the Rust community standard double license). Sometimes one, sometimes the other.
  • CONTRIBUTING.md - most important for you. Setup, conventions, PR process.
  • CODE_OF_CONDUCT.md - community standards.
  • Cargo.toml - package manifest. Read it: name, version, dependencies.
  • Cargo.lock - pinned dependency versions. Committed for binaries; not for libraries.
  • rust-toolchain.toml (optional) - pin the Rust version. rustup reads this and uses the right toolchain.
  • .gitignore - usually just /target and .idea/.
  • rustfmt.toml - formatter configuration.
  • clippy.toml - linter configuration.

.github/

  • workflows/*.yml - CI. Almost always runs cargo fmt --check, cargo clippy, cargo test. Reading the YAML tells you what your PR will be measured against.
  • ISSUE_TEMPLATE/, PULL_REQUEST_TEMPLATE.md - templates.

src/

The source code:

  • src/lib.rs - library entry point. Public modules listed here with pub mod foo;. The shape of this file is the project's public API.
  • src/main.rs - binary entry point (for a binary crate).
  • src/<module>.rs or src/<module>/mod.rs - modules.
  • src/bin/<name>.rs - extra binaries. Run with cargo run --bin <name>.

tests/

Integration tests. Each .rs file is compiled as a separate crate. They can only access the library's public API (good - keeps tests honest).

benches/

Benchmarks. Usually using the criterion crate. Run with cargo bench.

examples/

Runnable usage examples. Run with cargo run --example <name>. Read these first when you're trying to understand how to use a library.

target/

Build output. JARs, compiled crates, generated artifacts. Always gitignored. Can grow large; safe to cargo clean (re-downloads + rebuilds dependencies).

Conventions you'll meet

  • #![deny(missing_docs)] at the top of lib.rs - fails build if any public item lacks documentation. Common in well-maintained libraries.
  • #![warn(clippy::all)] - enable extra lint warnings.
  • #[macro_use] extern crate foo; - old syntax, predates use. Recognize.
  • pub use module::Thing; in lib.rs - re-export. Makes crate::Thing work even though Thing is defined in a submodule.

A worked walkthrough: BurntSushi/ripgrep or serde-rs/serde

Pick whichever you cloned in page 12. Apply:

  1. README - what does it do? Examples?
  2. Cargo.toml - module structure ([lib]? [[bin]]?), dependencies.
  3. src/lib.rs (or main.rs) - pub mod declarations show the public API.
  4. tests/ - pick one; trace it.
  5. .github/workflows/ - what does CI run?

Five minutes. Mental map.

Conventions in CONTRIBUTING.md

Open the file. Look for:

  • Setup. Usually cargo test.
  • Formatting. Almost always cargo fmt. Some projects require cargo fmt --check to pass.
  • Linting. Almost always cargo clippy. Some require zero warnings.
  • Commit message format. Some require Conventional Commits.
  • CHANGELOG. Some require a line under "Unreleased."
  • Sign-off / DCO. Rare in Rust OSS; common in some larger projects.

Follow them. Maintainers will be relieved.

CI configuration: what your PR will be measured against

Typical Rust CI:

- run: cargo fmt --all -- --check
- run: cargo clippy --all-targets --all-features -- -D warnings
- run: cargo test --all-features

So locally, before pushing, run:

cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test

If those pass locally, your PR (probably) passes CI.

Common helper tools

  • rustfmt - auto-formatter. Comes with rustup. cargo fmt.
  • clippy - linter. Comes with rustup. cargo clippy.
  • cargo-deny - checks for forbidden licenses, banned crates, RUSTSEC advisories.
  • cargo-udeps (nightly) - finds unused dependencies.
  • cargo-watch - re-run on file change: cargo watch -x check -x test.

The first two are universal. The others are project-specific.

Exercise

Use the project you picked in page 13.

  1. Clone locally.
  2. Walk the layout. Map files to categories above.
  3. Read CONTRIBUTING.md.
  4. Open one CI workflow YAML. Identify the commands.
  5. Run them locally:
    cargo fmt --check
    cargo clippy --all-targets
    cargo test
    
    Adjust to what CONTRIBUTING / CI specifies.
  6. Open the issue you tentatively picked in page 13. Identify the three files most likely involved (use grep and your guess).

You're ready to make a change.

What you might wonder

"What's rust-toolchain.toml?" Pins the Rust version. Looks like:

[toolchain]
channel = "1.80.0"
components = ["rustfmt", "clippy"]
rustup reads this and uses that version in this directory. Useful when a project needs nightly or a specific stable.

"What's [workspace]?" A multi-package project. Top-level Cargo.toml lists members. Each member is its own crate with its own Cargo.toml. Common in big projects (Tokio, Cargo itself).

Done

  • Recognize typical Rust project layout.
  • Locate every common file/folder.
  • Read CONTRIBUTING.md and CI workflows.
  • Make a confident guess at which files a change will touch.

Next: Your first contribution →

Comments