Quick Start
Want to learn Rust fundamentals quickly? This quick start touches 8-12 core Rust concepts with one example each. By the end, you’ll have practical touchpoints for the most important language features.
Prerequisites
Before starting, you should have:
- Completed Initial Setup - Rust toolchain installed and working
- A text editor or IDE (VS Code with rust-analyzer, IntelliJ with Rust plugin, or any editor)
- Basic understanding of programming concepts
- Willingness to write and run code
Learning Objectives
By the end of this tutorial, you will have touchpoints for:
- Variables and Mutability - Immutable by default, mut keyword
- Ownership - Move semantics, ownership rules
- Borrowing and References - Immutable and mutable references
- Structs - Define custom types, methods, associated functions
- Enums and Pattern Matching - Sum types, match expressions
- Error Handling - Result and Option types
- Traits - Define shared behavior
- Vectors and Collections - Dynamic arrays, HashMaps
- Iterators - Functional iteration
- Lifetimes - Reference validity
Learning Path
graph TD
A[Quick Start: Rust] --> B[Variables & Mutability]
A --> C[Ownership]
A --> D[Borrowing]
A --> E[Structs]
A --> F[Enums & Match]
A --> G[Error Handling]
A --> H[Traits]
A --> I[Collections]
A --> J[Iterators]
A --> K[Lifetimes]
B --> L[Beginner Tutorial]
C --> L
D --> L
E --> L
F --> L
G --> L
H --> L
I --> L
J --> L
K --> L
L --> M[By-Example: Rust]
style A fill:#e1f5ff
style L fill:#fff4e1
style M fill:#f0e6ff
Concept 1: Variables and Mutability - Immutable by Default
Rust variables are immutable by default for safety.
Example: Variables, Mutability, and Shadowing
fn main() {
// Immutable variable (default)
let x = 5;
println!("x = {}", x);
// x = 6; // Compile error! Cannot reassign immutable variable
// Mutable variable
let mut y = 10;
println!("y = {}", y);
y = 15; // OK - y is mutable
println!("y = {}", y);
// Shadowing - redefine variable
let z = 20;
let z = z + 5; // Different variable, same name
let z = z * 2; // Can change type too
println!("z = {}", z); // 50
// Type annotation
let age: u32 = 30;
let price: f64 = 19.99;
let name: &str = "Alice";
// Constants (must have type annotation, UPPERCASE convention)
const MAX_POINTS: u32 = 100_000;
println!("Max points: {}", MAX_POINTS);
// Destructuring
let (a, b, c) = (1, 2, 3);
println!("a={}, b={}, c={}", a, b, c);
// Type inference
let inferred = 42; // i32 inferred
let also_inferred = 3.14; // f64 inferred
}Key concepts: let, mut, shadowing, const, type inference
Key concepts: let, mut, shadowing, const, type inference
Concept 2: Ownership - Rust’s Core Feature
Ownership rules ensure memory safety without garbage collection.
Example: Ownership and Move Semantics
fn main() {
// Ownership rule 1: Each value has one owner
let s1 = String::from("hello"); // s1 owns the String
// Move semantics - ownership transfers
let s2 = s1; // s1's ownership moved to s2
// println!("{}", s1); // Compile error! s1 no longer valid
println!("{}", s2); // OK - s2 owns the data
// Copy types (stack-only data)
let x = 5;
let y = x; // Copy, not move (i32 implements Copy trait)
println!("x={}, y={}", x, y); // Both valid
// Functions take ownership
let s = String::from("hello");
takes_ownership(s); // s moved into function
// println!("{}", s); // Compile error! s no longer valid
// Return ownership
let s1 = gives_ownership(); // Function returns ownership
println!("{}", s1);
// Clone for deep copy
let s1 = String::from("hello");
let s2 = s1.clone(); // Explicit deep copy
println!("s1={}, s2={}", s1, s2); // Both valid
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
} // some_string dropped here
fn gives_ownership() -> String {
let some_string = String::from("hello");
some_string // Ownership returned to caller
}Key concepts: Ownership, move semantics, Copy trait, clone()
Concept 3: Borrowing and References - Temporary Access
Borrowing allows references without transferring ownership.
Example: Immutable and Mutable References
fn main() {
// Immutable reference (borrowing)
let s1 = String::from("hello");
let len = calculate_length(&s1); // Borrow with &
println!("Length of '{}' is {}", s1, len); // s1 still valid
// Mutable reference
let mut s = String::from("hello");
change(&mut s); // Mutable borrow with &mut
println!("{}", s); // "hello, world"
// Reference rules:
// Rule 1: Multiple immutable references OK
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2); // OK
// Rule 2: Only ONE mutable reference at a time
let r1 = &mut s;
// let r2 = &mut s; // Compile error! Can't have two mutable refs
println!("{}", r1);
// Rule 3: Can't mix mutable and immutable references
let r1 = &s;
// let r2 = &mut s; // Compile error! Can't borrow as mutable while immutable ref exists
println!("{}", r1);
// References must be valid (no dangling references)
// let reference_to_nothing = dangle(); // Compile error!
let no_dangle = no_dangle_example();
println!("{}", no_dangle);
}
fn calculate_length(s: &String) -> usize {
s.len() // Can read but not modify
} // s goes out of scope, but doesn't own data, so nothing dropped
fn change(s: &mut String) {
s.push_str(", world"); // Can modify through mutable reference
}
fn no_dangle_example() -> String {
let s = String::from("hello");
s // Return ownership, not reference
}Key concepts: & (immutable reference), &mut (mutable reference), borrowing rules
Concept 4: Structs - Custom Data Types
Define custom types to structure related data.
Example: Struct Definition and Methods
// Struct definition
struct User {
username: String,
email: String,
active: bool,
sign_in_count: u64,
}
// Tuple struct
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
// Unit struct (no fields)
struct AlwaysEqual;
// Implementation block for methods
impl User {
// Associated function (constructor)
fn new(username: String, email: String) -> User {
User {
username, // Field init shorthand
email,
active: true,
sign_in_count: 1,
}
}
// Method (takes &self)
fn is_active(&self) -> bool {
self.active
}
// Mutable method
fn deactivate(&mut self) {
self.active = false;
}
}
fn main() {
// Create struct instance
let mut user1 = User {
email: String::from("alice@example.com"),
username: String::from("alice"),
active: true,
sign_in_count: 1,
};
// Access fields
println!("{}", user1.email);
// Modify field (requires mut)
user1.email = String::from("newemail@example.com");
// Using constructor
let user2 = User::new(
String::from("bob"),
String::from("bob@example.com"),
);
// Struct update syntax
let user3 = User {
email: String::from("charlie@example.com"),
username: String::from("charlie"),
..user2 // Copy remaining fields from user2
};
// Method calls
println!("User active: {}", user1.is_active());
user1.deactivate();
println!("User active: {}", user1.is_active());
// Tuple structs
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}Key concepts: struct, impl, methods, associated functions, field init shorthand
Concept 5: Enums and Pattern Matching - Sum Types
Enums represent data that can be one of several variants.
Example: Enums and Match
// Basic enum
enum IpAddrKind {
V4,
V6,
}
// Enum with data
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
// Enum with multiple data types
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("Quit"),
Message::Move { x, y } => println!("Move to ({}, {})", x, y),
Message::Write(text) => println!("Write: {}", text),
Message::ChangeColor(r, g, b) => println!("Color: {}, {}, {}", r, g, b),
}
}
}
fn main() {
// Create enum values
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
// Match expression
let ip = IpAddr::V4(192, 168, 1, 1);
match ip {
IpAddr::V4(a, b, c, d) => {
println!("IPv4: {}.{}.{}.{}", a, b, c, d);
}
IpAddr::V6(addr) => {
println!("IPv6: {}", addr);
}
}
// Match with _ (catch-all)
let number = 7;
match number {
1 => println!("One"),
2 => println!("Two"),
3 | 4 | 5 => println!("Three, four, or five"),
_ => println!("Something else"),
}
// if let (concise pattern matching)
let some_value = Some(3);
if let Some(3) = some_value {
println!("Three!");
}
// Calling enum methods
let msg = Message::Write(String::from("Hello"));
msg.call();
}Key concepts: enum, match, pattern matching, if let
Concept 6: Error Handling - Result and Option
Rust uses Result and Option for explicit error handling.
Example: Result and Option Types
use std::fs::File;
use std::io::ErrorKind;
fn main() {
// Option<T> - value or None
let some_number = Some(5);
let no_number: Option<i32> = None;
// Unwrap Option
let x = some_number.unwrap(); // 5
// let y = no_number.unwrap(); // Panics!
// Match on Option
match some_number {
Some(value) => println!("Got value: {}", value),
None => println!("No value"),
}
// if let with Option
if let Some(value) = some_number {
println!("Value: {}", value);
}
// Result<T, E> - Ok(T) or Err(E)
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating file: {:?}", e),
},
other_error => panic!("Problem opening file: {:?}", other_error),
},
};
// unwrap and expect
let f = File::open("hello.txt").unwrap(); // Panics on error
let f = File::open("hello.txt")
.expect("Failed to open hello.txt"); // Panics with custom message
// ? operator (propagate errors)
let result = read_username_from_file();
match result {
Ok(username) => println!("Username: {}", username),
Err(e) => println!("Error: {}", e),
}
}
fn read_username_from_file() -> Result<String, std::io::Error> {
let mut f = File::open("username.txt")?; // ? returns error if Err
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err(String::from("Division by zero"))
} else {
Ok(a / b)
}
}Key concepts: Option<T>, Result<T, E>, match, unwrap, expect, ? operator
Concept 7: Traits - Shared Behavior
Traits define shared behavior across types.
Example: Trait Definition and Implementation
// Define trait
trait Summary {
fn summarize(&self) -> String;
// Default implementation
fn default_summary(&self) -> String {
String::from("(Read more...)")
}
}
struct NewsArticle {
headline: String,
content: String,
}
struct Tweet {
username: String,
content: String,
}
// Implement trait for NewsArticle
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}: {}", self.headline, self.content)
}
}
// Implement trait for Tweet
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("@{}: {}", self.username, self.content)
}
}
// Trait as parameter
fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
// Trait bound syntax
fn notify_bound<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
// Multiple trait bounds
fn notify_multiple<T: Summary + Clone>(item: &T) {
println!("{}", item.summarize());
}
fn main() {
let article = NewsArticle {
headline: String::from("Rust 2.0 Released"),
content: String::from("The Rust team announced..."),
};
let tweet = Tweet {
username: String::from("rustlang"),
content: String::from("New features in Rust!"),
};
println!("{}", article.summarize());
println!("{}", tweet.summarize());
notify(&article);
notify(&tweet);
}Key concepts: trait, impl Trait for Type, trait bounds, default implementations
Concept 8: Vectors and Collections - Dynamic Data
Rust provides powerful collection types.
Example: Vec, HashMap, HashSet
use std::collections::HashMap;
use std::collections::HashSet;
fn main() {
// Vector (dynamic array)
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
v.push(3);
// vec! macro
let v = vec![1, 2, 3, 4, 5];
// Access elements
let third = &v[2]; // Panics if out of bounds
let third = v.get(2); // Returns Option<&T>
match v.get(2) {
Some(value) => println!("Third element: {}", value),
None => println!("No third element"),
}
// Iterate
for i in &v {
println!("{}", i);
}
// Mutable iteration
let mut v = vec![1, 2, 3];
for i in &mut v {
*i += 50;
}
// HashMap (key-value)
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
// Access value
let team_name = String::from("Blue");
let score = scores.get(&team_name); // Returns Option<&V>
// Iterate over HashMap
for (key, value) in &scores {
println!("{}: {}", key, value);
}
// Update value
scores.insert(String::from("Blue"), 25); // Overwrite
// Insert if key doesn't exist
scores.entry(String::from("Red")).or_insert(50);
// HashSet (unique values)
let mut set = HashSet::new();
set.insert(1);
set.insert(2);
set.insert(2); // Duplicate ignored
println!("Set contains: {:?}", set); // {1, 2}
}Key concepts: Vec<T>, HashMap<K, V>, HashSet<T>, push, get, insert
Concept 9: Iterators - Functional Iteration
Iterators enable functional-style data processing.
Example: Iterator Methods
fn main() {
let v = vec![1, 2, 3, 4, 5];
// map - transform elements
let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();
println!("{:?}", doubled); // [2, 4, 6, 8, 10]
// filter - select elements
let evens: Vec<&i32> = v.iter().filter(|&&x| x % 2 == 0).collect();
println!("{:?}", evens); // [2, 4]
// fold - reduce
let sum: i32 = v.iter().fold(0, |acc, x| acc + x);
println!("Sum: {}", sum); // 15
// Chain operations
let result: Vec<i32> = v.iter()
.filter(|&&x| x % 2 == 0)
.map(|x| x * x)
.collect();
println!("{:?}", result); // [4, 16]
// any and all
let has_even = v.iter().any(|&x| x % 2 == 0);
let all_positive = v.iter().all(|&x| x > 0);
println!("Has even: {}, All positive: {}", has_even, all_positive);
// find
let first_even = v.iter().find(|&&x| x % 2 == 0);
match first_even {
Some(&num) => println!("First even: {}", num),
None => println!("No even numbers"),
}
// Infinite iterators
let first_10_evens: Vec<i32> = (0..).filter(|x| x % 2 == 0).take(10).collect();
println!("{:?}", first_10_evens);
}Key concepts: iter(), map, filter, fold, collect, lazy evaluation
Concept 10: Lifetimes - Reference Validity
Lifetimes ensure references are always valid.
Example: Lifetime Annotations
// Function with lifetime annotations
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
// Struct with lifetime
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention: {}", announcement);
self.part
}
}
fn main() {
let string1 = String::from("long string");
let string2 = String::from("xyz");
let result = longest(string1.as_str(), string2.as_str());
println!("Longest: {}", result);
// Lifetime with struct
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("No '.' found");
let i = ImportantExcerpt {
part: first_sentence,
};
// 'static lifetime (lives for entire program)
let s: &'static str = "I have a static lifetime.";
}Key concepts: Lifetime annotations 'a, lifetime elision, 'static
Summary
What you’ve touched:
- Variables and mutability (immutable by default,
mut) - Ownership (move semantics, single owner)
- Borrowing and references (
&,&mut, borrowing rules) - Structs (custom types, methods, associated functions)
- Enums and pattern matching (
enum,match,if let) - Error handling (
Result,Option,?operator) - Traits (shared behavior, trait bounds)
- Vectors and collections (
Vec,HashMap,HashSet) - Iterators (functional iteration,
map,filter) - Lifetimes (reference validity, lifetime annotations)
Key syntax learned:
// Variables
let x = 5; // Immutable
let mut y = 10; // Mutable
// Ownership
let s1 = String::from("hello");
let s2 = s1; // Move (s1 invalid)
let s3 = s2.clone(); // Deep copy
// References
let r = &s3; // Immutable reference
let r = &mut s3; // Mutable reference
// Structs
struct Point { x: i32, y: i32 }
impl Point {
fn new(x: i32, y: i32) -> Point {
Point { x, y }
}
}
// Enums
enum Option<T> {
Some(T),
None,
}
// Match
match value {
Some(x) => println!("{}", x),
None => println!("No value"),
}
// Result
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 { Err("div by zero".to_string()) }
else { Ok(a / b) }
}Next Steps
Want comprehensive Rust mastery?
Prefer code-first learning?
- By-Example Tutorial - Heavily annotated Rust examples
Want to understand Rust philosophy?
- Overview - Why Rust exists and when to use it
Quick Reference Card
Essential Syntax
// Variables
let x = 5; // Immutable
let mut y = 10; // Mutable
const MAX: u32 = 100;
// Functions
fn add(a: i32, b: i32) -> i32 {
a + b // No semicolon for return
}
// Control flow
if x > 0 {
println!("Positive");
}
for i in 0..5 {
println!("{}", i);
}
// Collections
let v = vec![1, 2, 3];
let mut map = HashMap::new();
map.insert("key", "value");
// Error handling
let result = File::open("file.txt")?;Common Patterns
// Iterate and transform
let doubled: Vec<_> = nums.iter().map(|x| x * 2).collect();
// Filter
let evens: Vec<_> = nums.iter().filter(|&&x| x % 2 == 0).collect();
// Pattern matching
match value {
Ok(v) => println!("Success: {}", v),
Err(e) => println!("Error: {}", e),
}
// Option handling
if let Some(x) = optional_value {
println!("{}", x);
}
// Reference patterns
let x = &value; // Immutable borrow
let y = &mut value; // Mutable borrow
This quick start provides touchpoints for essential Rust operations. For production work, explore the beginner tutorial for comprehensive coverage and by-example content for heavily annotated code patterns.