Compiling and Running Programs
In Rust, turning source code into a running program is a two-step process: first, the compiler translates .rs source files into a standalone native binary executable; second, the operating system loads and executes that binary. You can perform these steps either manually with rustc (for single files) or automatically with cargo (for projects).
Workflow Comparison: rustc vs cargo
1. Single-File Manual Workflow (`rustc`):
main.rs ──[ rustc --edition 2024 main.rs ]──> ./main ──[ ./main ]──> Program Output
2. Project Workflow (`cargo`):
src/main.rs ──[ cargo build ]──> target/debug/<pkg> ──[ ./target/debug/<pkg> ]──> Output
│ ^
└────────────────────────────[ cargo run ]─────────────────────────────────────┘
(Compiles if needed + runs in one command)
| Workflow | Command to Compile Only | Command to Compile & Run | Output Binary Location |
|---|---|---|---|
Single file (rustc) |
rustc --edition 2024 file.rs |
rustc --edition 2024 file.rs && ./file |
./file (current directory) |
| Cargo debug mode | cargo build |
cargo run |
target/debug/<package_name> |
| Cargo release mode | cargo build --release |
cargo run --release |
target/release/<package_name> |
| Type-check only (no binary) | cargo check |
N/A | Metadata only in target/debug/deps/ |
Compiling and Running a Single File with rustc
For quick experiments or single-file programs that do not depend on external crates from crates.io, compile the file with rustc and execute the resulting binary:
// greet.rs
fn main() {
let args: Vec<String> = std::env::args().collect();
println!("Executable path: {}", args[0]);
println!("Arguments passed: {:?}", &args[1..]);
}
rustc --edition 2024 greet.rs
./greet alpha beta
Executable path: ./greet
Arguments passed: ["alpha", "beta"]
Once ./greet is compiled, it is a self-contained native machine binary. You can run ./greet as many times as you like—or copy it to another machine with the same OS and CPU architecture—without invoking rustc again.
Compiling and Running a Project with cargo
Inside a Cargo project directory, cargo build compiles the project without running it, while cargo run compiles any modified files and immediately launches the binary:
cargo build
./target/debug/my_project
Or in a single step:
cargo run
Compiling my_project v0.1.0 (/Users/username/my_project)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.18s
Running `target/debug/my_project`
Hello, world!
Incremental Rebuilds
If you run cargo run a second time without modifying any .rs files or Cargo.toml, Cargo detects that the binary in target/debug/ is already up to date, skips rustc completely, and executes the binary immediately:
cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/my_project`
Hello, world!
Suppressing Cargo Status Lines (--quiet / -q)
By default, Cargo prints Compiling, Finished, and Running status lines to standard error (stderr). Pass -q (or --quiet) to suppress Cargo's status messages so only your program's output appears in the terminal:
cargo run -q
Hello, world!
Passing Command-Line Arguments to Your Program
When using cargo run, place a double dash (--) between Cargo's own flags and the arguments intended for your program:
cargo run -q -- --input data.csv --limit 50
Everything before -- (-q) configures cargo, and everything after -- (--input data.csv --limit 50) is passed directly to your binary's main function via std::env::args().
Exit Codes and Process Termination
When main() finishes normally, the Rust runtime exits the process with status code 0 (indicating success to the operating system shell).
- If the program panics (
panic!("...")) or returns anErrfromfn main() -> Result<(), E>, the process exits with a non-zero status code (101for panics,1forErr). - In shell scripts, you can inspect the exit code of the last run via
$?(echo $?).