What Rust is
Rust is a compiled systems language that aims for the speed and control of C and C++ without their memory bugs. It does this through ownership: every value has exactly one owner, borrowing is checked when the program is compiled, and the compiler refuses code that could use freed memory or race on shared data. Writing it feels like working with a very careful reviewer — the error messages are unusually detailed and often suggest the fix — and once a program compiles it tends to behave. The price is a steeper learning curve than most languages.
Where Rust is used
- Systems software
- The Linux kernel and Android both now contain Rust code, and Microsoft has rewritten parts of Windows in it, in each case to remove memory-safety bugs from code that runs with high privileges.
- Command-line tools
- Fast start-up, a single binary and a strong set of libraries for argument parsing and terminal output make Rust a common choice for developer tools.
- Network services
- Async Rust with the Tokio runtime is used for web servers, proxies and services where memory use and latency matter.
- WebAssembly
- Rust compiles cleanly to WebAssembly, so performance-critical parts of a web application — image editing, simulations, games — can be written in Rust and run inside the browser.
- Embedded and firmware
- Rust runs on microcontrollers without an operating system (no_std), giving the same memory guarantees on hardware where a crash is expensive to track down.
Your first Rust program
Saved as main.rs. Run it with the steps in the next section.
fn main() {
let name = "Ravi";
let year = 2026;
println!("Hello from Rust.");
println!("{name} started learning in {year}.");
println!("Two years on, it will be {}.", year + 2);
}What it prints
Hello from Rust.
Ravi started learning in 2026.
Two years on, it will be 2028.- Line 1 declares main, the function every Rust program starts in.
fnintroduces a function and the braces hold its body. - Lines 2 and 3 create two variables with
let. Rust infers the types —nameis a string slice (&str) andyearan integer (i32by default) — and both are immutable: assigning toyearlater would be a compile error unless it had been declaredlet mut year. - Line 5 prints a line.
println!ends in an exclamation mark because it is a macro rather than a function, which is what lets it check the format string at compile time. - Line 6 puts variables straight into the text:
{name}and{year}are replaced by their values. This inline form works for plain variable names. - Line 7 uses an empty
{}placeholder filled by the expression after the comma, because an expression such asyear + 2cannot go inside the braces directly.
Run Rust on your own computer
Rust is installed with rustup, the official toolchain manager, which brings the compiler (rustc), the build tool and package manager (cargo) and the documentation in one step. Nearly all Rust work goes through cargo.
Install rustup
On macOS and Linux, run the command below in a terminal; it downloads rustup and installs the current stable toolchain. On Windows, download and run rustup-init.exe from rust-lang.org/tools/install; it tells you if it needs the Visual Studio C++ build tools, which the default Windows toolchain links against, and offers to install them.
Shellcurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shCheck it works
Open a new terminal so the PATH change is picked up. rustc --version shows the compiler; cargo is the command you will type most.
Shellcargo --versionCreate a project
This makes a hello folder containing Cargo.toml, which holds the project's metadata and dependencies, and src/main.rs, which already contains a hello-world. Replace the contents of src/main.rs with the program above.
Shellcargo new helloRun it
Run this inside the hello folder. cargo compiles the project, prints a few lines about what it is doing, then runs the result. The first build of a project takes longer than later ones. cargo build compiles without running, and cargo build --release turns on optimisations for a program you mean to ship.
Shellcargo runKeep the toolchain current
Rust publishes a new stable release every six weeks, and this one command updates everything rustup installed. For an editor, install rust-analyzer, the official language server; it shows the compiler's own error messages inline as you type.
Shellrustup update
A learning order for Rust
Stages, not a timetable. Each one exists because the next would not make sense without it, and how long each takes depends on how much you write.
Stage 1. Cargo, printing and variables
- cargo new, build and run
- let and let mut
- integers, floats, bool and char
- println! and format strings
- reading compiler errors and cargo check
Immutability by default is the first thing that trips people, and the compiler's messages are the main teaching tool, so learning to read them carefully comes first.
Stage 2. Control flow and functions
- if as an expression
- loop, while and for over ranges
- match
- functions and return values
- expressions versus statements
In Rust almost everything is an expression — if and match produce values — which changes how you write even simple code. match is the pattern-matching construct you will use constantly.
Stage 3. Ownership and borrowing
- moves
- references: & and &mut
- the borrowing rules
- String versus &str
- lifetimes in outline
This is the heart of Rust and the reason it is harder than other languages. Expect to spend real time here and to argue with the borrow checker; it is teaching you what memory-safe code looks like.
Stage 4. Structs, enums and collections
- struct and impl
- enums that carry data
- Option and Result
- Vec, HashMap and String
- iterators and closures
Option and Result replace null and exceptions, and are why Rust programs handle missing values and failures explicitly. Iterators are how idiomatic Rust loops, and they compile down to the same code as a hand-written loop.
Stage 5. Errors, traits and generics
- the ? operator
- custom error types
- traits and impl Trait
- generics and trait bounds
- Debug, Clone and Display
Traits are Rust's answer to both interfaces and inheritance, and generics are checked against them at compile time. This is the stage where your code starts to look like the standard library's.
Stage 6. Modules, crates and beyond
- modules and pub
- dependencies from crates.io
- cargo test
- Box, Rc and RefCell
- threads, Send and Sync, then async
Real programs span files and pull in crates. Smart pointers and the threading traits are the advanced corners of ownership, and async is a large enough topic to deserve its own pass once the rest is solid.
Mistakes beginners make in Rust
- Assigning to an immutable variable
- let count = 0; followed by count += 1; fails with 'error[E0384]: cannot assign twice to immutable variable `count`'. Variables are immutable unless declared with let mut. The compiler's message includes the fix, and getting used to reading those messages is half the learning curve.
- Using a value after it has been moved
- let a = String::from("hi"); let b = a; println!("{a}"); gives 'error[E0382]: borrow of moved value: `a`'. A String owns its memory, and assigning it hands ownership to b, so a is no longer valid. Clone it with a.clone() if you really need two copies, or borrow it with &a if you only need to look at it.
- Holding a mutable and a shared borrow at the same time
- Taking &mut v while an earlier &v is still in use produces 'error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable'. The rule is one mutable borrow or any number of shared borrows, never both at once. The usual fix is to shorten the life of the first borrow, and the error points at the lines involved.
- Confusing String and &str
- Passing a String where a function wants &str, or the reverse, gives 'error[E0308]: mismatched types' with a note such as 'expected `&str`, found `String`'. A String owns its text and can grow; a &str is a borrowed view of text that lives somewhere else. Functions should usually take &str, and callers pass &my_string.
- Calling unwrap and crashing
- Calling .unwrap() on an Option or Result panics when the value is None or Err, with 'called `Option::unwrap()` on a `None` value'. That is fine in a quick experiment, but in real code use match, if let or the ? operator to handle the missing case instead of assuming it never happens.
Strengths and trade-offs
Where it is strong
- Memory safety without a garbage collector: no use-after-free, no data races and no null, all enforced at compile time.
- Performance in the same class as C and C++, with the same control over memory layout and allocation.
- Error messages that explain what went wrong and usually propose the fix, plus cargo, which makes building, testing and dependency management one consistent tool.
- One language from firmware to WebAssembly to web servers, with the same guarantees everywhere.
Where it is not
- The learning curve is steep. Ownership, borrowing and lifetimes have no direct equivalent in other mainstream languages, and the compiler rejects code that would be accepted in C or Go until you learn to structure it differently.
- Compile times are long compared with Go or C, and noticeably so on large projects with many dependencies.
- Slower to write: satisfying the compiler takes longer than in a garbage-collected language, which matters when you are prototyping.
- Async Rust and some advanced trait patterns are genuinely hard, and the ecosystem for graphical interfaces and games is younger than C#'s or C++'s.
Who Rust is for
Rust is the right next language for programmers who already know one language and want to understand memory and concurrency properly, or who work in systems, embedded, security or performance-sensitive code. It can be a first language for a patient, curious learner who wants the hard things taught explicitly, but most people find the ownership rules easier to appreciate after seeing the bugs they prevent in C or the runtime costs they avoid in a garbage-collected language. If you want to get something working quickly, want to do data work or scripting, or want a web front-end or a mobile app, start with Python, JavaScript or C# and come back later.
Questions about learning Rust
- Is Rust a good first language?
- It can be, but it is a demanding one. The compiler's error messages are unusually good and the tooling is consistent, which helps a lot. But ownership and borrowing are ideas you cannot skip, and they are easier to appreciate when you have already met the problems they solve. Many people learn Python or Go first and Rust second, and there is nothing wrong with that order. If you do start with Rust, expect slower early progress and read the official book in order.
- Do I need to know C or C++ first?
- No. Rust does not build on C, and its syntax owes as much to the ML family of languages as to C. What helps is having met pointers, the stack and the heap once, because Rust's rules make more sense when you know what they are protecting you from. If you have never seen those ideas, the ownership chapter of the official book introduces them from scratch.
- What is the borrow checker?
- The part of the compiler that enforces the ownership rules: every value has one owner, you can hold either one mutable reference or any number of shared references, and a reference can never outlive the value it points to. It runs at compile time, so it costs nothing when the program runs, and it rejects programs that could corrupt memory before they ever start. Fighting it is the normal experience at first; the errors become rarer as your code naturally starts to follow the rules.
- Should I use cargo or rustc directly?
- cargo, almost always. rustc main.rs works for a single file and is worth seeing once, but cargo handles dependencies, tests, documentation and release builds, and every Rust project and tutorial assumes it. The only reason to reach for rustc on its own is a quick one-off experiment.
The primary source
When this guide and the official documentation disagree, the documentation is right and we would like to hear about it.