Skip to content
BiliMind
Roadmap

How to learn Rust

This path assumes you can already write code in some language, but not that you know C++ or systems programming. It gets you from a blank cargo project to reading and modifying real Rust codebases in 120-180 hours of deliberate practice.

Hours

120-180 hours

Calendar

about 20 weeks at 7 hours a week

Stages

7

Start from

You need to have written programmes in at least one other language: enough to know what variables, functions, loops and modules are. You do not need prior C, C++ or systems programming experience; that belief keeps people away longer than it should.

Why the usual order fails

Rust is not a language you can learn by reading. Ownership, borrowing and lifetimes are not extra features bolted onto a familiar language; they are the language. Every function signature, every data structure, every error message is shaped by them. Most roadmaps treat ownership as one chapter among many, to be completed and then left behind. That is why so many learners stall around week four: they have memorised syntax but cannot make a simple programme compile without fighting the borrow checker. The syntax is the easy part. The borrow checker is the subject.

This path inverts the usual order. In week one you will not be writing 'hello world' and moving on; you will be writing small functions that move, borrow and clone values, and reading compiler errors until they become predictable. That means the first fortnight is slower than most plans. It also means the next two months are faster, because you are not re-learning every concept through ownership later. Most courses front-load syntax and types, then hit ownership at week six, by which point every example you have written has quietly avoided the hard cases. Here the hard cases come first.

The path, in the order it has to be walked.

Each stage assumes the one above it and nothing below it. The check at the foot of each is the point — you are meant to fail it, go back, and try again before moving on.

  1. Ownership as the compiler sees it

    weeks 1-3

    This stage is not about comfort with syntax; it is about training yourself to predict which code moves, borrows or copies. It sits first because every later concept builds on it, and because delaying it is the single most common reason people quit Rust.

    • Move semantics for non-Copy types
    • Copy vs Clone traits and when each applies
    • Immutable and mutable borrows, including borrow checker rules
    • String vs &str and when each is used
    • Slices and references into owned data
    • The compiler error messages E0382, E0502 and E0506
    • Shadowing and its interaction with ownership

    You can now: Write a function that takes a String, returns its length and still lets the caller use the original afterwards, without cloning, by borrowing correctly.

  2. Types that make memory explicit

    weeks 4-6

    Rust's type system is doing ownership work everywhere. This stage is where you stop fighting it and start using it. Enums and pattern matching come here because they are the natural way to model data that can be in one of several states, which is most data.

    • Structs with owned and borrowed fields
    • Enums with data, including Option and Result
    • Pattern matching with match, if let and while let
    • The Result type and the ? operator
    • Deriving Debug, Clone, PartialEq and when not to derive
    • Generic functions and structs with trait bounds
    • The Copy trait and why some types do not implement it
    • Iterators and the Iterator trait

    You can now: Write a small CLI programme that reads a file path from argv, returns Result from main, and handles a missing file with a match on the error type, not just a panic.

  3. Lifetimes and the borrow checker's view of time

    weeks 7-9

    Most learners meet lifetimes as a compiler complaint and never get past it. This stage is about reading lifetime elision rules and writing function signatures that express the actual relationship between inputs and outputs. It sits after ownership because lifetimes are just ownership over time.

    • Lifetime elision rules in function signatures
    • Explicit lifetimes in structs that hold references
    • Lifetime subtyping where one reference outlives another
    • Writing functions that return references into arguments
    • The difference between 'static and long-lived, and why string literals are 'static
    • Lifetime errors E0495 and E0106
    • Covariance and contravariance in practice, without the words

    You can now: Write a struct that holds a &str field and a function that returns the longer of two &str arguments, with explicit lifetimes, and explain why the compiler accepts it.

  4. Shared state without the usual pain

    weeks 10-12

    Rust's rules around interior mutability and shared ownership are where the borrow checker meets real programmes. This stage is about Rc, RefCell, Arc and Mutex, and about knowing which one you need without guessing. It sits after lifetimes because these types exist to work around the rules you have just learned.

    • Rc and Arc for shared ownership
    • RefCell and interior mutability
    • Mutex and poisoning
    • Channels for sending data between threads
    • Combining Arc and Mutex, and why the combination is so common
    • Send and Sync traits and what they guarantee
    • Choosing between Rc and Arc by whether threads are involved
    • The deadlock that Rust does not prevent

    You can now: Build a multithreaded programme where several threads push to a shared Vec via Arc<Mutex<Vec<T>>>, and explain why the Mutex is inside the Arc and not the other way round.

  5. Errors and the type system as documentation

    weeks 13-15

    Rust's error handling is not about try-catch; it is about making failure a type. This stage is where you learn the patterns that make Rust code readable: custom error types, the thiserror and anyhow crates, and when to use which. It sits here because you need the previous stages to understand what a good error type looks like.

    • Custom error types with the Error trait
    • The thiserror crate for deriving Display and Error
    • The anyhow crate for application-level errors
    • When to use a library error enum vs anyhow
    • The From trait for converting between error types
    • Panicking vs returning Result, and when each is right
    • The unwrap and expect methods and why they are for prototyping only
    • The map_err and or_else combinators

    You can now: Rewrite a small CLI tool that previously used unwrap to use a custom error enum with thiserror, and handle a missing file, a permission error and an invalid UTF-8 path as three distinct error variants.

  6. Async and the executor model

    weeks 16-18

    Rust's async is not like JavaScript's. It is a set of building blocks, not a runtime, and you must understand what the executor is doing before async code makes sense. This stage is late because it builds on ownership, lifetimes and error handling.

    • Futures and the Future trait, and why they are lazy
    • The Tokio runtime and what it does
    • async fn and await, and how they desugar
    • The Send constraint on futures and why it matters
    • Choosing between threads and async for a task
    • The select! macro and timeouts
    • Streams and the Stream trait
    • The async and await syntax and what it does not do

    You can now: Write a small async programme that fetches two URLs concurrently with Tokio, waits for both with a timeout, and prints each result or a timeout error, without blocking the thread.

  7. Reading and writing real Rust

    weeks 19-20

    The final stage is about moving from toy programmes to the code other people write. You need to read crate documentation, understand public APIs, and contribute small fixes. This is where you find out whether you actually know Rust or just memorised syntax.

    • Reading crate documentation and understanding the public API
    • Cargo workspaces and feature flags
    • The borrow checker in the context of a large codebase
    • Writing tests with the #[test] attribute and assert!
    • Clippy and rustfmt and why they are not optional
    • Reading and understanding the standard library's source for small types
    • Contributing a small fix to a real crate

    You can now: Pick a small crate on crates.io with fewer than 500 lines, read its source, and submit a pull request that fixes a documentation error or adds a test, and get it merged.

The hours are fixed. The calendar is yours.

The same path at three speeds, and what actually goes wrong at each.

3 hours a week

At 3 hours a week, this path takes 40-60 weeks. The problem is not the hours; it is the gap between sessions. Ownership requires daily repetition for the first three weeks, and with only 3 hours a week you are likely to forget the borrow checker rules between sessions and spend most of each session re-learning them. You will finish, but slowly, and the early stages will feel harder than they should.

8 hours a week

At 8 hours a week, the path takes 15 to 22 weeks. This is the pace the numbers above assume, and it is sustainable if you can give ownership two to three focused sessions a week in the first month. The hard part is not the total hours but the first three weeks, when the borrow checker feels like a wall. If you can get through that at this pace, the rest is steady.

20 hours a week

At 20 hours a week, you can finish in 6 to 9 weeks. This pace works if you are already a strong programmer and can treat Rust as a full-time project. The risk is that ownership and lifetimes need time between sessions to consolidate, and at 20 hours a week you may find yourself compiling code without fully understanding why it works. Slowing down to 12-15 hours a week for the first three weeks is often faster in the end.

What goes wrong

Four ways people lose a year on this.

Each of these is appealing for a real reason, which is why they are common. What they cost is the part nobody mentions.

  • Learning Rust syntax before ownership

    Most tutorials start with variables, loops and functions, and only introduce ownership in chapter four or five. This is appealing because it lets you write running code immediately and feel productive. The cost is that every example you write before ownership uses a simplified model that is wrong. When the borrow checker arrives, you have to unlearn habits, and the compiler errors feel personal. The syntax is not the hard part; ownership is.

  • Treating the borrow checker as an adversary

    People new to Rust often write code, get a borrow checker error, and immediately look for a way to silence it: cloning, wrapping in Rc, or using unsafe. This is appealing because it feels like progress. The cost is that you never learn the underlying rule, and every future error takes longer to fix. The borrow checker is telling you something true about your data. Reading the error message carefully, and then the relevant chapter, is almost always faster than working around it.

  • Using unsafe to make the compiler shut up

    When the borrow checker refuses to accept code that you believe is correct, the temptation is to wrap it in unsafe and move on. This is appealing because the code now compiles, and the urge to finish the feature is strong. The cost is that you have thrown away the main reason to use Rust. Unsafe is for the few cases where the compiler genuinely cannot see that the code is sound, not for learner frustration. If you are reaching for unsafe in your first six months, you are solving the wrong problem.

  • Skipping error handling until a real project needs it

    Many learners write every fallible function with unwrap and tell themselves they will replace it later. This is appealing because the code is shorter and the happy path works immediately. The cost is that later never comes, and when a real error happens in a programme you have written, it panics with a cryptic message. Error handling is not a feature you add at the end; it is the type system doing its job, and you should be writing Result from the first function.

How to know it worked.

Not a syllabus you tick off — six things to attempt. If one of them is still hard, the stage it belongs to is the one to go back to.

  1. Write a function that takes a String and returns its length without moving the String.
  2. Explain why a &str cannot outlive the String it borrows from.
  3. Write a struct that holds a reference and give it the correct lifetime.
  4. Change a Vec<i32> to a Vec<&str> and explain what you had to change elsewhere.
  5. Write a function that returns the longer of two &str arguments with explicit lifetimes.
  6. Build a multithreaded programme with Arc<Mutex<Vec<T>>> and explain why it compiles.
Questions

Questions about learning Rust.

The five we hear most. The intro film answers most of them in a minute and a half.

how long does it take to learn rust

If you already can write code in another language, expect 120-180 hours of deliberate practice to reach genuine working competence. That is about 20 weeks at 7 hours a week. The first three weeks are the hardest, because ownership and the borrow checker will not make sense for a while. Rushing this period makes the total time longer.

do i need to know c or c++ before learning rust

No, and the idea that you do is one of the most common reasons people delay. Rust's ownership model is closer to C++'s RAII than to C's manual malloc and free, but you do not need to have learned either. What you need is to have written programmes in some language, so you know what variables, functions and types are. Prior C++ experience can help with the mental model, but it can also bring habits that Rust rejects, so it is not a clear win.

is rust harder than python

Yes, for the first few weeks, and for a different reason than most people think. Python lets you write a working programme in an hour, but it does not ask you to know what memory is doing. Rust asks you to make ownership explicit, and that means you cannot write much until you understand it. After the first few weeks, Rust's type system often makes programmes easier to reason about, but the upfront cost is real and much higher than Python's.

why does the rust compiler keep rejecting my code

Because the borrow checker is enforcing rules that every Rust programme must follow, and your code is violating one of them. The error message usually names the rule: moved value, borrowed value after move, or lifetime mismatch. The most common reason beginners keep getting rejected is that they are trying to write code in the style of a language without ownership. Slowing down and reading the full error message, including the help text, is the fastest way to stop seeing them.

can i learn rust as my first programming language

You can, but the path is longer and harder than learning Python or JavaScript first. Rust expects you to already know what a variable is, what a type is and what a function does, and it layers ownership on top of that. If you are starting from zero, expect to spend 200 to 300 hours before you can write a small programme that does something useful. Learning a simpler language first is not a requirement, but it usually saves time overall.

A path is not a course.

This is the shape of Rust, not the teaching of it. Hand the same path to BiliMind and it writes the lessons onto it — in the form each one calls for, with practice that gets marked and a schedule that bends when your week does. Mapping a course is free and unlimited.

This roadmap was drafted on 18 September 2026 by the same engine that builds BiliMind courses, and reviewed before publishing. Hours are honest estimates for working competence, not for a demo — treat them as a starting point and adjust them against your own first fortnight.

The intro · 1:25