The basics
What is an RS file?
An .rs file holds Rust source code: text that a programmer writes and the Rust compiler translates into machine code. The result is a native program (an .exe on Windows, a binary with no extension on macOS and Linux) or a library that other Rust code uses. You can open the file in any text editor and read it, but you can't run it directly.
Rust is used where speed and reliability both matter: command-line tools, web servers, browsers, operating system components and code compiled to WebAssembly. Its compiler checks at compile time that memory is used safely, without a garbage collector, which is why Rust code often takes longer to get compiling and then has fewer crashes.
Three things that explain a lot of confusion:
- A crate is the unit of compilation, not a file. The compiler starts from one root file (usually
src/main.rsorsrc/lib.rs) and pulls in the other.rsfiles that the root declares withmod. A.rsfile that nothing declares is silently ignored. - Most projects are built with Cargo, not by calling the compiler. Cargo reads
Cargo.toml, downloads dependencies from crates.io and runsrustcwith the right options. - The edition changes how a file is read. The same code can compile under one edition and fail under another. It's set in
Cargo.toml, not in the.rsfile, so a single file on its own doesn't say which one it needs.
Anatomy
What's inside a Rust file
A Rust file is a list of items: use declarations, types, functions, modules and constants, in any order. Statements only appear inside functions. Here is a small but complete program that converts a temperature, with a unit test at the bottom, part by part.
//! A tiny temperature converter.
use std::env;
#[derive(Debug)]
struct Reading {
celsius: f64,
}
impl Reading {
fn fahrenheit(&self) -> f64 {
self.celsius * 9.0 / 5.0 + 32.0
}
}
fn parse(arg: &str) -> Result<Reading, String> {
let celsius: f64 = arg.parse().map_err(|_| format!("not a number: {arg}"))?;
Ok(Reading { celsius })
}
fn main() {
let arg = env::args().nth(1).unwrap_or_else(|| String::from("20"));
match parse(&arg) {
Ok(r) => println!("{}°C is {:.1}°F", r.celsius, r.fahrenheit()),
Err(e) => eprintln!("error: {e}"),
}
}
#[cfg(test)]
mod tests {
#[test]
fn boiling() {
assert_eq!(super::parse("100").unwrap().fahrenheit(), 212.0);
}
}- 1
//! A tiny temperature converter.A documentation comment
//!documents the file or crate it's in;///documents the item below it.cargo docturns both into HTML pages. Ordinary comments use//or/* … */. - 2
use std::env;A use declaration
Brings a module from the standard library (
std) into scope, so the code can writeenv::args()instead of the full path. - 3
#[derive(Debug)]An attribute
#[…]attaches information to the next item.deriveasks the compiler to write a trait implementation, here the one that lets{:?}print the struct. - 4
struct Reading { celsius: f64 }A struct
A type with named fields. Rust has no classes: data lives in structs and enums, behaviour in
implblocks and traits. - 5
impl Reading { fn fahrenheit(&self) … }A method
&selfmeans the method borrows the value instead of taking ownership of it. The last expression without a semicolon is the return value. - 6
-> Result<Reading, String>A Result
Rust has no exceptions for ordinary errors. A function that can fail returns
Ok(value)orErr(error), and the caller has to deal with both. - 7
map_err(…)?The ? operator
If the parse failed,
?returns the error fromparsestraight away; otherwise it unwraps the value.format!builds aString, and{arg}inserts the variable. - 8
fn main()The entry point
Where the program starts. A crate whose root file has
fn maincompiles to an executable; a library's root,lib.rs, has none. - 9
match parse(&arg) { Ok(r) => …, Err(e) => … }A match
Picks a branch by the shape of the value. The compiler refuses a
matchthat doesn't cover every case. - 10
println!(…) / eprintln!(…)Macros
The
!marks a macro, code that expands at compile time. These print to standard output and standard error; the format string is checked when the program is compiled. - 11
#[cfg(test)] mod tests { #[test] … }A unit test
Compiled only by
cargo testorrustc --test, so it adds nothing to the normal program. Tests live in the same file as the code they test.
How to
How to compile and run a .rs file
Install Rust with rustup from the official website; it installs rustc, Cargo, rustfmt and Clippy, and rustup update keeps them current. The compiler also needs a linker: on Windows the installer asks you to install Visual Studio's build tools, on macOS run xcode-select --install, and on Linux install GCC or Clang (on Ubuntu, build-essential).
| rustc | cargo | |
|---|---|---|
| Compile one file | rustc --edition 2024 main.rs | Needs a package: cargo new demo, then put the code in src/main.rs |
| Run it | ./main (macOS, Linux) or main.exe (Windows) | cargo run builds and runs in one step |
| Optimised build | rustc -O main.rs | cargo build --release, output in target/release/ |
| Default edition | 2015, unless you pass --edition | Whatever Cargo.toml says; cargo new writes the newest |
| Dependencies from crates.io | Not handled | Listed in Cargo.toml, downloaded and built for you |
| Check without building | No equivalent | cargo check type-checks fast without producing a program |
The edition trap
Run rustc file.rs without --edition and the file is compiled as Rust 2015. Modern code can then fail with errors like expected identifier, found keyword or async fn is not permitted in Rust 2015. Pass --edition 2024, or build with Cargo.
There's no interpreter: every change means compiling again, though cargo run only recompiles what changed. The compiled program runs on its own, without Rust installed, on the operating system and processor it was built for.
Projects
Cargo.toml, main.rs, lib.rs and mod.rs
A Cargo package is a folder with a Cargo.toml manifest at the top and the .rs files under src/. Cargo finds targets by file name, so the layout is a convention you rarely configure:
converter/
├── Cargo.toml the manifest: name, edition, dependencies
├── Cargo.lock exact dependency versions (written by Cargo)
├── src/
│ ├── main.rs the program's entry point (fn main)
│ ├── lib.rs a library crate, if the package has one
│ ├── units.rs module "units", declared with: mod units;
│ └── bin/
│ └── extra.rs a second executable
├── tests/ integration tests
├── examples/ example programs (cargo run --example name)
└── target/ build output, not committed to version control[package]
name = "converter"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1", features = ["derive"] }src/main.rsis the root of the executable,src/lib.rsthe root of a library crate. A package can have both: putting the logic inlib.rsand keepingmain.rsthin makes it testable fromtests/.- Modules map to files.
mod units;inmain.rsmakes the compiler look forsrc/units.rs, or the oldersrc/units/mod.rs. Having both is a compile error. Submodules ofunitsgo in asrc/units/folder. - File and crate names: use lowercase with underscores (
order_items.rs), because a module name must be a valid identifier. Package names may use hyphens; in code,my-toolbecomesmy_tool. Cargo.lockrecords the exact versions of every dependency. Commit it for programs, so every build uses the same code.build.rsat the package root is a build script: Cargo compiles and runs it before building the package.
Versions
Rust editions: 2015, 2018, 2021 and 2024
Rust ships a new compiler version every six weeks, and every version compiles all editions. An edition is an opt-in set of changes that wouldn't be backwards compatible, such as new keywords. Crates of different editions work together: a 2024 program can depend on a 2015 library.
| Edition | Stable since | Notable changes |
|---|---|---|
| 2015 | Rust 1.0 (May 2015) | The original language. Still the default when no edition is given. |
| 2018 | Rust 1.31 | New module paths (crate::, no extern crate), dyn Trait, async and await become keywords, foo.rs next to a foo/ folder instead of mod.rs |
| 2021 | Rust 1.56 | Closures capture individual fields, array.into_iter() yields values, TryFrom and TryInto in the prelude, consistent panic! formatting |
| 2024 | Rust 1.85 (February 2025) | gen reserved as a keyword, unsafe extern blocks, new temporary scopes for if let and tail expressions, a Rust-version-aware dependency resolver, a new rustfmt style edition |
To move a package to a newer edition, run cargo fix --edition, change edition in Cargo.toml, then build, test and run cargo fmt. cargo fix handles most changes; macros and doc tests sometimes need editing by hand.
Library features are tied to the compiler version, not the edition. A method added in Rust 1.87, such as u32::is_multiple_of, doesn't exist on 1.86 whatever the edition. Cargo's rust-version field states the oldest compiler a package supports.
Tooling
cargo fmt, cargo clippy and cargo test
| Command | What it does |
|---|---|
cargo fmt | Reformats every .rs file in the package in the community style (4-space indents, a 100-column limit) with rustfmt. cargo fmt --check only reports differences, which is what CI usually runs. Settings go in rustfmt.toml. |
cargo clippy | Runs Clippy, a collection of several hundred lints for likely bugs, slow code and unidiomatic style. cargo clippy -- -D warnings turns every warning into an error. |
cargo test | Builds the package in test mode and runs unit tests (#[test] functions inside #[cfg(test)] modules), integration tests in tests/ and the code examples in /// documentation comments. |
cargo check | Type-checks without generating machine code. Much faster than a build, so editors run it constantly. |
cargo doc --open | Builds HTML documentation from the doc comments and opens it. |
Without Cargo, rustc --edition 2024 --test file.rs builds a test runner for a single file: run the program it produces and it prints one line per test. Doc tests need Cargo, because their examples are compiled against the library as if they were outside code.
Related formats
The files around a .rs file, and how Rust compares
| File | What it is |
|---|---|
.rs | Rust source code, plain text. |
Cargo.toml | The package manifest, in TOML: name, edition, dependencies, build settings. |
Cargo.lock | Exact versions and checksums of every dependency, written by Cargo. |
rust-toolchain.toml | Pins the compiler version or channel (stable, nightly) for a project; rustup reads it. |
.rlib | A compiled Rust library, an intermediate file in target/. Not portable between compiler versions. |
| An executable | The compiled program: name.exe on Windows, name on macOS and Linux. Runs without Rust installed. |
.wasm | WebAssembly. Rust compiles to it for browsers and other WebAssembly runtimes. |
| Rust (.rs) | C++ (.cpp) | Go (.go) | |
|---|---|---|---|
| Compiled to | Native machine code | Native machine code | Native machine code |
| Memory management | Ownership and borrowing, checked at compile time | Manual or smart pointers | Garbage collector |
| Build tool and packages | Cargo and crates.io, built in | Several (CMake, vcpkg, Conan…) | The go command and modules, built in |
| Header files | None | .h, .hpp | None |
| Official formatter | rustfmt | None (clang-format is common) | gofmt |
Troubleshooting
The Rust compile errors people hit most
Every Rust error has a code; rustc --explain E0382 prints a full explanation with examples.
| Error | Usual cause | Fix |
|---|---|---|
| E0382: borrow of moved value | A String, Vec or other owned value was passed by value, so it moved, then used again | Pass a reference (&value), or .clone() it if you need two copies |
| E0502: cannot borrow as mutable because it is also borrowed as immutable | A reference into a collection is still in use while the collection is changed | Finish using the reference first, or copy the value out |
| E0308: mismatched types | A value of one type where another is expected, such as "5" for an i32 | Convert explicitly: "5".parse::<i32>() |
expected identifier, found keyword async | Modern code compiled as Rust 2015 by a bare rustc call | rustc --edition 2024, or build with Cargo |
| E0432: unresolved import / can't find crate | A crate from crates.io used without Cargo, or not listed in Cargo.toml | cargo add name, then build with Cargo |
| E0583: file not found for module | mod name; with no name.rs or name/mod.rs next to the file | Create the file, or fix the module name |
linker cc not found (or link.exe on Windows) | No C linker installed | Install build-essential, the Xcode command-line tools or Visual Studio's C++ build tools |
sample-compile-error.rs below reproduces E0382 and E0502 on purpose, with the fix for each in a comment.
How to
How to open and edit a .rs file, and is it safe?
- Read it: any text editor shows it: Notepad, TextEdit (in plain-text mode), or a browser if it's online. Rust files are always UTF-8.
- Edit it: VS Code with the rust-analyzer extension gives completion, inline errors and go-to-definition; RustRover, Zed, Neovim and Helix also support Rust. rust-analyzer needs the file to be part of a Cargo package to understand it fully.
- Phones and tablets: you can read
.rsfiles in any text or code viewer, but compiling needs a computer, or an online playground such as play.rust-lang.org.
Opening a .rs file in an editor is safe: it's text, and nothing runs. Building a project you don't trust is another matter:
- Build scripts and procedural macros run code on your computer.
cargo buildcompiles and runs a package'sbuild.rsand the procedural macros of every dependency, with your user's permissions. Reading the code first, or building in a container or virtual machine, is the safe way to try an unknown project. - Dependencies are other people's code. Check the names of crates you add: look-alike names (typosquatting) are a known way to slip in malicious packages. Tools such as
cargo auditcheckCargo.lockagainst a database of security advisories. unsafedoesn't mean dangerous to open. It marks blocks where the programmer, not the compiler, guarantees memory safety. Reviewers look at those blocks first.
Free downloads
Sample Rust files to download
Free sample Rust files for testing compilers, editors, CI pipelines, syntax highlighters, upload filters and size limits. Every sample except the deliberately broken one compiles with Rust 1.97 and passes cargo fmt --check and cargo clippy -- -D warnings in a 2024-edition package.
Loading…What's inside sample-main.rs
The file is commented: open it in a text editor and each numbered comment marks one of these parts. Compile it with rustc --edition 2024 sample-main.rs and run the result: it prints a total per person, and reports one line it skipped.
Comments 1 to 4: types
A
usedeclaration, a struct and an enum with#[derive], and animplblock whosematchturns a text label into an enum value.Comment 5: an error type
An enum of the ways parsing can fail, and a
Displayimplementation that turns each into a readable message.Comments 6 and 7: Result, ? and Option
parse_linereturns aResultand useslet … elseand?;parse_centsreturns anOptionand keeps money in whole cents with overflow-checked arithmetic.Comments 8 and 9: borrowing and iterators
A function that borrows the list and returns a
BTreeMapwhose keys borrow from it, thenfilter,mapandsumon an iterator.Comments 10 and 11: data and main
The input is a string constant with five lines, one of them invalid.
maincollects the good lines, prints the bad one to standard error witheprintln!, and prints totals: €49.75 for Ada Example, €138.90 for Ben Example.Comment 12: unit tests
Three
#[test]functions in a#[cfg(test)]module.rustc --edition 2024 --test sample-main.rs -o sample-testsbuilds a test runner; running it reports 3 passed.
All sample Rust files
Commented sample program
sample-main.rs · 5.9 KB (6,059 bytes)
A complete command-line program: structs, enums, traits,
Resultand?, borrowing, iterators and a unit-test module, with numbered comments explaining each part. Compiles withrustcalone, no dependencies.- Checking a compiler, CI job or syntax highlighter on modern Rust
- Teaching the building blocks of a Rust program
Library crate (lib.rs) with doc tests
sample-lib.rs · 4.6 KB (4,734 bytes)
A library root as it would sit in
src/lib.rsof a package namedsample-lib: public functions, a trait with a default method, a generic function,//!and///documentation with examples thatcargo testruns as doc tests, and unit tests.- Testing `cargo test`, `cargo doc` and doc-test handling
- An example of a documented public API
UTF-8 with BOM and CRLF line endings
sample-unicode-bom-crlf.rs · 1.9 KB (1,913 bytes)
Starts with the UTF-8 byte order mark
EF BB BFand uses Windows line endings throughout. Prints six sample strings (Latin, Greek, Japanese, Arabic, emoji) with byte and character counts, and shows thatrustcignores the BOM and reads CRLF as LF, even inside a string literal.- Testing editors, linters and diff tools on BOM and CRLF files
- Showing `len()` (bytes) vs `chars().count()`
Deliberately broken file (does not compile)
sample-compile-error.rs · 1.3 KB (1,296 bytes)
Clearly labelled at the top: it fails with E0382 (use of a moved value) and E0502 (conflicting borrows), and its comments give the fix for each. Nothing in it is harmful.
- Checking how editors and CI jobs report compile errors
- Teaching ownership and borrowing errors
100 KB Rust file
sample-100kb.rs · 100 KB (102,400 bytes)
A program with a table of the first 13,200 prime numbers that checks every one, padded with comment lines to exactly 102,400 bytes. Needs Rust 1.87 or later.
- Upload and size-limit tests
- Editor, highlighter and formatter performance
Ways to test with them
- Compiler and CI: build
sample-main.rsandsample-100kb.rs; both should compile without warnings and print their results.sample-compile-error.rsshould fail with exactly two errors, E0382 and E0502. - Editions: compile
sample-main.rswith plainrustcand then with--edition 2024. It useslet … else, which works in every edition, so both succeed; it's a quick way to show that a barerustcdefaults to 2015 without breaking anything. - Encodings and line endings: open
sample-unicode-bom-crlf.rsin your editor, run it throughrustfmtand your diff tool, and check the BOM and the CRLF endings survive (or are converted the way you expect). - Upload limits:
sample-100kb.rsis exactly 102,400 bytes, for testing a limit at the boundary.
Free to use for testing, teaching and demos, commercially or not, with no attribution needed. A link back to this page is always appreciated.
Put it online
Have a Rust file? Give it a link.
Drop a .rs file on 99helpers and it gets its own web address. Anyone who opens the link reads the code as plain text in their browser: it is never compiled or run, which makes it a simple way to share code for review, teaching or a bug report.
Questions
Rust files, frequently asked
What is a .rs file?
A Rust source file: plain-text code in the Rust programming language. A compiler turns it into a program or a library; on its own it doesn't run.
How do I run a .rs file?
Install Rust with rustup, then compile and run it: rustc --edition 2024 main.rs followed by ./main (or main.exe on Windows). For a project with a Cargo.toml, run cargo run in its folder instead.
How do I open a .rs file?
Any text editor opens it, since it's plain UTF-8 text. For writing Rust, VS Code with the rust-analyzer extension or RustRover give completion and inline errors.
What's the difference between rustc and cargo?
rustc is the compiler and works on one crate at a time. Cargo is the build tool and package manager: it reads Cargo.toml, downloads dependencies and calls rustc for you. Most projects use Cargo.
What's the difference between main.rs and lib.rs?
src/main.rs is the root of an executable and contains fn main. src/lib.rs is the root of a library that other code can use. A package can have both.
Which Rust edition should I use?
2024 for new code: it's what cargo new sets. Editions are chosen per package in Cargo.toml, and packages of different editions work together, so existing code can stay on its edition.
Is it safe to open a .rs file?
Yes, opening it in an editor only shows text. Building an untrusted Cargo project is riskier: its build scripts and procedural macros run code on your computer during the build.
Can I convert a .rs file into an .exe?
Yes, that's what compiling does. On Windows, rustc main.rs or cargo build --release produces an .exe that runs on other Windows computers without Rust installed.
Related formats
Other file types explained
Keep going
Related
Sources
- The Rust Reference: input format (UTF-8, BOM, CRLF)
- The Rust Edition Guide
- Rust Blog: Announcing Rust 1.85.0 and Rust 2024
- The Cargo Book: package layout
- The Rust Programming Language: test organization
- The Rust Programming Language: installation
Last reviewed 27 October 2026.