Skip to content
Learn Rust
Theme

More than one program in a project

Soon you’ll want a second program next to your main one: a seed script, a migration tool, a small demo. Cargo lets one project hold as many as you like.

RememberEvery .rs file directly in src/bin/ (or src/bin/<name>/main.rs) is its own program with its own main. Shared code goes in src/lib.rs.

Here’s the layout. Each program has its own main, and they all share the code in lib.rs:

Project layout
tools/
├── examples/
│   └── demo.rs                 example "demo"
├── src/
│   ├── bin/
│   │   └── seed.rs             program "seed"
│   ├── lib.rs                  shared code, used as tools::...
│   └── main.rs                 program "tools"
├── Cargo.lock                  created by Cargo
└── Cargo.toml
Rust
// Shared code. Every program in this package can use it as `tools::...`.
pub fn banner(program: &str) -> String {
format!("== {program} ==")
}
$ cargo run --bin seed
== seed ==
inserted 3 sample rows
$ cargo run --bin tools
== server ==
serving requests...
$ cargo run --example demo
== demo ==
  • src/main.rs is the program named after the package, here tools.
  • src/bin/seed.rs is a program called seed. Add as many files there as you need. A bigger one can be a folder, src/bin/seed/main.rs, with its own modules (Try it #1).
  • examples/ holds programs that show how to use your code. cargo build and cargo run don’t build them, so they suit demos. cargo test does compile them, to check they still build (Try it #3).
  • src/lib.rs makes the package a library too. Every program in the package can use it by the package name, like tools::banner. The Go version below puts the same code in a package, internal/banner.

For how files, crates and mod fit together, see Files and modules and the Modules lesson.

Go
module example.com/tools
go 1.27
$ go run ./cmd/seed
== seed ==
inserted 3 sample rows
$ go run ./cmd/server
== server ==
serving requests...

Where extra programs live in other languages

Section titled “Where extra programs live in other languages”
Language Where extra programs live Run one
Rust src/bin/seed.rs (each with main), examples/demo.rs cargo run --bin seed, cargo run --example demo
Go a folder with package main; cmd/seed/ is the usual place go run ./cmd/seed
Python any file; the if __name__ == "__main__": guard stops import seed from running it python3 seed.py
TypeScript any .ts file node seed.ts (Node 22.18 or newer strips the types; I ran 24)
Java any class with main java Seed.java, or javac Seed.java then java Seed
Kotlin any file with fun main kotlinc Seed.kt -include-runtime -d seed.jar, then java -jar seed.jar; a Gradle build runs the one mainClass it names
C# a project’s Program.cs; a second program is usually a second project in the solution dotnet run --project Seed
C and C++ one source file with main per executable gcc seed.c -o seed, g++ seed.cpp -o seed; in CMake, add_executable(seed seed.c) (not run)

Every command above ran in this course’s image, except CMake, which is not installed there. The C# one ran as dotnet run --no-restore --project Seed from an offline project template, because the image has no network.

Gotcha

Plain cargo run stops working

Nothing in your code changed, but Cargo stops and asks a question.

No direct equivalent in Go.No direct equivalent in Kotlin.Not run in Python on this page.Not run in TypeScript on this page.Not run in Java on this page.Not run in C# on this page.Not run in C on this page.Not run in C++ on this page.
RustCargo.toml✗ cargo refuses
[package]
name = "tools"
version = "0.1.0"
edition = "2024"
[dependencies]
Gono equivalent

Go has no default-run either: go run . at the module root has no main package to run, so you name the folder, as in go run ./cmd/seed.

Kotlinno equivalent

Gradle's run task uses the one mainClass you configured, so you pick it in the build file.

There is no Python version of this one.

There is no TypeScript version of this one.

There is no Java version of this one.

There is no C# version of this one.

There is no C version of this one.

There is no C++ version of this one.

What does the compiler say? Guess first, then open.
$ cargo run
error: `cargo run` could not determine which binary to run. Use the `--bin` option to specify a binary, or the `default-run` manifest key.
available binaries: seed, tools
What happens in Go

Go has no default-run either: go run . at the module root has no main package to run, so you name the folder, as in go run ./cmd/seed.

What happens in Kotlin

Gradle's run task uses the one mainClass you configured, so you pick it in the build file.

Why it looks fine

With one program, cargo run knew what to run. Nothing about your code changed, so this feels like it broke for no reason.

Why it looks fine

With one program, cargo run knew what to run. Nothing about your code changed, so this feels like it broke for no reason.

Why it looks fine

With one program, cargo run knew what to run. Nothing about your code changed, so this feels like it broke for no reason.

Why it looks fine

With one program, cargo run knew what to run. Nothing about your code changed, so this feels like it broke for no reason.

Why it looks fine

With one program, cargo run knew what to run. Nothing about your code changed, so this feels like it broke for no reason.

Why it looks fine

With one program, cargo run knew what to run. Nothing about your code changed, so this feels like it broke for no reason.

What Rust sees

Now there are two programs, tools and seed, and Cargo won't guess which one you meant.

Why Rust says no

Picking one for you could run the wrong thing, such as a seed script instead of the server. Name the program each time with --bin, or set a default once in Cargo.toml.

The fix

Compiles and runs.

Rust, fixedCargo.toml
[package]
name = "tools"
version = "0.1.0"
edition = "2024"
default-run = "tools"
[dependencies]
$ cargo run
== server ==
serving requests...

Run these in a copy of the project above.

  1. Make a program a folder. Add src/bin/report/main.rs and src/bin/report/table.rs, with mod table; in main.rs. Cargo finds report with no change to Cargo.toml, and mod table; loads the file beside it.
Rust
// A program that is a folder: its own modules live next to this file.
mod table;
fn main() {
println!("{}", tools::banner("report"));
table::print(&[("seed", 3), ("report", 1)]);
}
$ cargo run --bin report
== report ==
seed    3
report  1
  1. Mistype a name. Run cargo run --bin sed. Cargo says there is no such program and suggests seed.
$ cargo run --bin sed
error: no bin target named `sed` in default-run packages

help: a target with a similar name exists: `seed`
  1. Find out what cargo build skips. Run cargo build, then ls target/debug/examples. The folder is empty until cargo build --example demo makes demo. Running cargo test in the same project makes demo appear there as well.
terminal
$ cargo build
$ ls target/debug/examples   # the hash differs between toolchains, so it is masked
$ cargo build --example demo
$ ls target/debug/examples   # the hash differs between toolchains, so it is masked
demo
demo-<hash>
demo-<hash>.d
demo.d