What makes Rust so difficult to learn?

Table of contents

What makes Rust so difficult to learn?

Rust is difficult, but not impossible. After working through The Rust Book , I understand why so many developers are drawn to it.

If you already know why it’s hard and want learning resources, skip to the end. I wouldn’t recommend Rust as a first language - it’s easier to learn if you already know another language, especially Go .

Rust was created by Graydon Hoare after an elevator incident caused by poor memory management, likely in C or C++.

Rust was created by Graydon Hoare after a problem with an elevator

Rust was created by Graydon Hoare after a problem with an elevator

Concepts that make Rust hard

Ownership and borrowing

Rust’s main goal is to eliminate common memory errors. It enforces a strict ownership model: each value has one owner. Ownership can be transferred (moved) or borrowed, but only one owner exists at a time. This makes memory leaks much harder to create.

Lifetimes

Lifetimes tell the compiler how long references are valid when it can’t infer them. Once understood, they’re manageable, but they can make code harder to read at first.

How it feels when you first read about lifetimes

How it feels when you first read about lifetimes

You don’t need to write them often, but you’ll see them in function signatures:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { 
    if x.len() > y.len() {
        x 
    } else {
        y 
    }
}

Smart pointers (Box, RefCell, Cell, Arc, Mutex, Rc)

Rust has multiple smart pointers for heap allocation and shared access. Knowing when to use each - and how they differ - takes time. Mutexes in particular are common in this space.

When you learn Rust for the first time it feels like this
let value = Rc::new(RefCell::new(5));

Resources that helped:

let value = Rc::new(RefCell::new(5));

Tip: While researching about smart pointers I found this video, and I understood them perfectly after watching it.

Also check this simple summary of Box, Arc, Rc, RefCell and Mutex.

Macros

Macros generate Rust code at compile time. They’re common in Rust, but can be hard to read if you’re not used to #[derive(...)] or macro invocations with !.

#[derive(Debug)]
enum List {
    Cons(Rc<RefCell<i32>>, Rc<List>),
    Nil,
}

Closures

Rust closures look different from JavaScript. They use |...| for parameters and often need move to transfer ownership.

std::thread::spawn(move || {
    println!("captured {data:?} by value")
}).join().unwrap();

It takes a bit of getting used to, but the syntax becomes familiar quickly.

Error handling

Rust doesn’t use try/catch. Errors and missing values are handled explicitly with Option and Result.

This guide explains it well.

Option

Represents a value (Some) or absence (None):

let x: Option<i32> = Some(13);
let y: Option<i32> = None;

Result

Represents success (Ok) or failure (Err):

fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err("Cannot divide by zero".to_string())
    } else {
        Ok(a / b)
    }
}

expect

Returns the value or panics with a message:

let x = Some(5).expect("Value not found");

The ? operator

Propagates errors automatically:

fn read_file() -> Result<String, std::io::Error> {
    let content = std::fs::read_to_string("file.txt")?;
    Ok(content)
}

Traits, impl, and generics

Combining traits (similar to interfaces) with generics can get complex. Here’s an example from Rustlings:

use std::fmt::Display;

struct HogwartsReportCard<T> {
    grade: T,
    student_name: String,
    student_age: u8,
}

impl<T: Display> HogwartsReportCard<T> {
    fn print(&self) -> String {
        format!(
            "Hogwarts' student: {} ({}) - achieved a grade of {}",
            &self.student_name, &self.student_age, &self.grade,
        )
    }
}

Where to learn Rust

This isn’t a weekend task. Here are solid resources:

Books and guides

Videos

Practice

  • Rustlings - Small exercises to practice while reading the book
Rustling's reward after finishing all exercises

Rustling’s reward after finishing all exercises

  • New to programming: Start with The Rust Book
  • Know a low-level language (C/C++): Try Easy Rust or Comprehensive Rust
  • Coming from JavaScript/Python: Use Comprehensive Rust or Easy Rust, and supplement with the YouTube channels above

Final thoughts

Rust isn’t the hardest language out there - Haskell is a bigger paradigm shift. It’s harder than Go or Python, and arguably harder than C to learn, but much easier to write safe code in than C.

Rust is elegant, but productivity sometimes matters more. The TypeScript team chose Go over Rust for the TypeScript compiler , which says a lot about tradeoffs.

The hard parts (ownership, borrowing, lifetimes) are a small fraction of the language. Once past them, most Rust syntax is straightforward. In exchange, you avoid many memory leaks and null pointer issues. For the right projects, that tradeoff is worth it.

Eduardo Zepeda
Eduardo Zepeda
Just call me Ed. developer and Linux (GNU/Linux) preacher. I like boring and predictable frameworks, the more opinionated and less boilerplate the better. I think Django is the best framework for MVPs. I used to believe in the goodness of cryptocurrencies outside of monetary speculation.