Workflow: Beginner Path ​
Updated Mar 2026Overview ​
A structured learning roadmap from zero Rust knowledge to building real applications. Follow this path in order.
Learning Roadmap ​
Phase 1: Setup and Basics (Week 1-2) ​
Install Rust ​
bash
# Install rustup (manages Rust versions)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Verify installation
rustc --version
cargo --version
# Install essential tools
rustup component add clippy rustfmtHello World ​
rust
fn main() {
println!("Hello, Rust!");
}bash
cargo new hello_rust
cd hello_rust
cargo runVariables, Types, Functions ​
rust
fn main() {
// Immutable by default
let x = 5;
let mut y = 10; // Mutable
y += x;
// Type annotation
let z: f64 = 3.14;
// Shadowing (re-declare)
let spaces = " ";
let spaces = spaces.len(); // Different type!
println!("{y}, {z}, {spaces}");
}
// Functions with types
fn add(a: i32, b: i32) -> i32 {
a + b // No semicolon = expression (returned)
}
// Control flow
fn classify(n: i32) -> &'static str {
if n < 0 {
"negative"
} else if n == 0 {
"zero"
} else {
"positive"
}
}Key Exercises ​
- Write a temperature converter (Fahrenheit to Celsius)
- Generate the nth Fibonacci number
- Print lyrics to "The Twelve Days of Christmas"
Phase 2: Ownership (Week 3-4) ​
This is the most important concept in Rust. Spend extra time here.
Must-Know Concepts ​
rust
fn main() {
// MOVE: String is heap-allocated
let s1 = String::from("hello");
let s2 = s1; // s1 is invalid now
// println!("{s1}"); // Compile error!
// COPY: i32 is stack-only
let n1 = 42;
let n2 = n1; // Both valid
println!("{n1} {n2}");
// BORROW: use without taking ownership
let s3 = String::from("world");
let len = calculate_length(&s3); // Borrow
println!("{s3} has length {len}"); // Still valid!
// SLICE: reference to a portion
let s4 = String::from("hello world");
let hello = &s4[0..5]; // &str slice
println!("{hello}");
}
fn calculate_length(s: &String) -> usize {
s.len()
}Phase 3: Data Modeling (Week 5-6) ​
rust
// Structs for structured data
struct User {
name: String,
email: String,
active: bool,
}
impl User {
fn new(name: String, email: String) -> Self {
User { name, email, active: true }
}
fn deactivate(&mut self) {
self.active = false;
}
}
// Enums for variants
enum Shape {
Circle(f64), // radius
Rectangle(f64, f64), // width, height
Triangle { base: f64, height: f64 },
}
impl Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
Shape::Triangle { base, height } => 0.5 * base * height,
}
}
}
fn main() {
let shapes: Vec<Shape> = vec![
Shape::Circle(5.0),
Shape::Rectangle(4.0, 6.0),
Shape::Triangle { base: 3.0, height: 8.0 },
];
for shape in &shapes {
println!("Area: {:.2}", shape.area());
}
}Phase 4: Error Handling + Traits (Week 7-8) ​
rust
use std::fmt;
use std::fs;
// Custom error
#[derive(Debug)]
enum AppError {
IoError(std::io::Error),
ParseError(String),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AppError::IoError(e) => write!(f, "IO error: {e}"),
AppError::ParseError(msg) => write!(f, "Parse error: {msg}"),
}
}
}
impl From<std::io::Error> for AppError {
fn from(e: std::io::Error) -> Self {
AppError::IoError(e)
}
}
// Trait for shared behavior
trait Describable {
fn describe(&self) -> String;
}
impl Describable for User {
fn describe(&self) -> String {
format!("{} ({})", self.name, self.email)
}
}Phase 5: Collections + Iterators (Week 9-10) ​
rust
use std::collections::HashMap;
fn main() {
// Vec
let mut scores = vec![85, 92, 78, 95, 88];
scores.sort();
let average: f64 = scores.iter().sum::<i32>() as f64 / scores.len() as f64;
// HashMap
let mut word_count: HashMap<String, u32> = HashMap::new();
let text = "hello world hello rust hello";
for word in text.split_whitespace() {
*word_count.entry(word.to_string()).or_insert(0) += 1;
}
// Iterator chains
let top_words: Vec<_> = word_count
.iter()
.filter(|(_, &count)| count > 1)
.map(|(word, count)| format!("{word}: {count}"))
.collect();
println!("Average: {average:.1}");
println!("Top words: {top_words:?}");
}Phase 6: First Project (Week 11-12) ​
Pick one and build it:
| Project | Skills Used |
|---|---|
| CLI todo app | Structs, enums, file I/O, serde, clap |
| Grep clone | File I/O, iterators, pattern matching, CLI args |
| URL shortener API | Axum/Actix, HashMap, async, HTTP |
| Markdown to HTML converter | String processing, enums, parsing |
Starter: CLI Todo App ​
rust
use serde::{Serialize, Deserialize};
use std::fs;
#[derive(Serialize, Deserialize, Debug)]
struct Todo {
id: u32,
title: String,
done: bool,
}
#[derive(Serialize, Deserialize, Debug, Default)]
struct TodoList {
todos: Vec<Todo>,
next_id: u32,
}
impl TodoList {
fn add(&mut self, title: String) {
self.next_id += 1;
self.todos.push(Todo {
id: self.next_id,
title,
done: false,
});
}
fn complete(&mut self, id: u32) -> bool {
if let Some(todo) = self.todos.iter_mut().find(|t| t.id == id) {
todo.done = true;
true
} else {
false
}
}
fn save(&self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
let json = serde_json::to_string_pretty(self)?;
fs::write(path, json)?;
Ok(())
}
fn load(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
let data = fs::read_to_string(path)?;
let list: TodoList = serde_json::from_str(&data)?;
Ok(list)
}
}Resources ​
| Resource | Type | URL |
|---|---|---|
| The Rust Book | Official guide | doc.rust-lang.org/book |
| Rust by Example | Interactive examples | doc.rust-lang.org/rust-by-example |
| Rustlings | Exercises | github.com/rust-lang/rustlings |
| Exercism Rust Track | Practice problems | exercism.org/tracks/rust |
| Rust Playground | Online compiler | play.rust-lang.org |