Vibe Engines
YouTube
← All decks

Learn Rust: ideas and compiler errors

Loading your review…

All 70 cards in this deck

Every idea of the Learn Rust course, chapter by chapter, the compiler errors in its atlas, and each lesson’s self-check questions once you reach it. Answer each question before you open it — then run the review above, which schedules every card with spaced repetition so it comes back right before you would forget it.

  1. What is the difference between compiling and running a program?

    Compiling is the compiler turning your text into a program the computer can run, checking it on the way. Running is that program doing its steps. Rust catches as much as it can at the first moment.

  2. Which three parts of a Rust error message do you read first?

    The error code and message (what is wrong), the --> arrow (file, line, column), and the ^^^ underline (exactly where). The help: line under it often names the fix.

  3. How does a Rust function give back a value?

    Its last expression, written WITHOUT a semicolon, is the value it returns (or you write return value;). The -> Type after the parameters says what kind of value comes back.

  4. What does mut change about a let?

    A plain let name cannot be given a new value. let mut allows it. Rust makes "this can change" visible where the name is made.

  5. What do the numbers in i32, u8 and u64 mean?

    How many bits the number uses (32, 8, 64). i means it can be negative, u means it cannot. More bits, bigger range: a u8 holds 0 to 255.

  6. What happens when a u8 holding 255 has 1 added?

    In a debug build the program stops with a panic ("attempt to add with overflow"). In a release build it wraps round to 0. Methods like checked_add and saturating_add let you choose.

  7. When should a value be a const rather than a let?

    When it is a fixed fact of the program that never changes, like a name or a limit. A const is written in UPPER_CASE and always has its type written out.

  8. What does it mean that a match must be exhaustive?

    Every possible value must be handled by some arm. If one is missing the program does not compile (E0004), so a forgotten case is caught before it can happen.

  9. Stack or heap: where does a String keep its letters?

    On the heap, because text can grow. The String variable itself sits on the stack: a pointer to the letters, a length and a capacity (24 bytes on a 64-bit machine).

  10. State the one-owner rule.

    Every value has exactly one owner at a time. Assigning it or passing it to a function moves it (the old name can no longer be used). When the owner goes away, the value is freed, exactly once.

  11. What does moving a String actually copy?

    Only the 24-byte handle (pointer, length, capacity). The letters on the heap stay where they are, so moving a huge message is as cheap as moving a tiny one.

  12. Why is `let b = a;` fine for an i32 but a move for a String?

    An i32 is Copy: its bits are the whole value, so both names stay valid. A String owns heap memory; duplicating it would copy the letters, which Rust only does when you write .clone().

  13. What is the borrowing rule?

    At any moment a value may have many shared borrows (&T) or exactly one mutable borrow (&mut T), never both. That is what stops one part of a program changing data another part is reading.

  14. When does a borrow end?

    At its last use, not at the end of the block. That is why code that changes a value after the last read of a borrow still compiles.

  15. String or &str: which should a function parameter usually be?

    &str. A String owns its text and can grow; a &str is a borrowed view (pointer and length) of text someone else owns. A &String turns into a &str for you, so &str accepts both.

  16. Why can slicing text by a number crash the program?

    Text is stored as UTF-8, where some letters (é, emoji) take several bytes. Slicing in the middle of one is not a valid string, so Rust panics. Find positions with methods like find or split instead.

  17. When is .clone() the right call?

    When you genuinely need a second, independent owner (Chatterbox keeps your last message for /again and another copy in the history). As a reflex to silence E0382 inside a loop it is a hidden copy every time round.

  18. struct or enum: which says "all of these" and which says "one of these"?

    A struct holds all of its fields at once (a Message has an author AND a text). An enum is exactly one of its variants at a time (a Command is Help OR Quit OR Say).

  19. What is &self in a method?

    The value the method was called on, borrowed. In message.shout(), self is message. &mut self would let the method change it; self alone would take ownership.

  20. Why does Rust have no null?

    Missing is a type: Option<T> is Some(value) or None, and the compiler will not let you use the value until you have handled None. A whole family of crashes becomes a compile error.

  21. What is if let for?

    Matching ONE pattern and ignoring the rest: if let Some(text) = &last { … } runs only when last holds something, with text bound to it.

  22. iter(), iter_mut() or into_iter(): what does each hand out?

    iter() lends each item (&T), iter_mut() lends each item to change (&mut T), into_iter() gives each item away (T), using up the collection.

  23. Why does HashMap::get return an Option?

    Because the key might not be there. get gives Some(&value) or None; map[key] with square brackets panics on a missing key instead.

  24. What does it mean that a closure captures a variable?

    It uses a variable from the code around it. |m| m.text.contains(&word) borrows word from where the closure was written, so the closure can be passed around and still see it.

  25. Why does `history.iter().map(f)` on its own do nothing?

    Iterators are lazy: map only describes a step. The work happens when a consumer such as collect, sum or a for loop asks for the items.

  26. Result or panic: which is for problems the program should expect?

    Result: bad input, a missing file, a failed network call are normal events the caller should handle. A panic is for a bug, a state the program should never reach.

  27. What does `?` do on a Result?

    On Ok(v) it gives v and carries on; on Err(e) it returns from the function right there with that error (converted with From). It only works in a function that itself returns a Result or Option.

  28. What turns a function into a test, and how do you run it?

    The #[test] attribute, usually inside #[cfg(test)] mod tests. cargo test builds every test and runs it; assert_eq!(got, expected) fails the test with both values printed when they differ.

  29. What do cargo fmt and cargo clippy do?

    cargo fmt rewrites your code in the standard layout. cargo clippy reads it like an experienced reviewer and warns about mistakes and clumsy patterns, often with the fix (cargo clippy --fix applies many).

  30. Why can main.rs not read a field of a struct defined in another module?

    Everything in a module is private unless marked pub. Mark the field pub, or give the struct a pub method that returns it (error E0616 otherwise).

  31. Does a generic function like last_n<T> cost anything when the program runs?

    No. The compiler writes a separate copy for each type you actually use it with (monomorphisation), so a generic call is as fast as one written by hand for that type.

  32. Box<dyn Replier>: what does each part mean?

    dyn Replier is "some type that implements the Replier trait, decided while the program runs". Box puts that value on the heap so the variable has one fixed size whatever is inside.

  33. What is a lifetime, in one sentence?

    The compiler’s name for how long a borrow is valid. Most are worked out for you; you write one (like 'a) when a function returns a borrow and the compiler cannot tell which input it borrows from.

  34. What does #[derive(Serialize, Deserialize)] give a struct?

    The code to turn it into data formats such as JSON and back, written by serde at compile time from the struct’s field names and types.

  35. cargo run or cargo build --release: which do you give to someone else?

    The release build: it takes longer to compile and runs much faster. It is one file in target/release/ that runs without Rust installed.

  36. What does calling an async fn give you, and when does its code run?

    A future: a recipe for the work, not the work. Nothing runs until something awaits it (.await), and a runtime such as tokio drives it.

  37. What does .await do?

    Pauses this async code until the future is ready, and lets other work run in the meantime. It is only allowed inside async code (E0728 otherwise).

  38. Why does tokio::spawn need async move?

    The task may keep running after the function that started it returns, so it cannot borrow from that function. move hands it ownership of what it uses (E0373 otherwise).

  39. How does a tokio mpsc channel tell the receiver an answer is complete?

    recv() returns None once every Sender has been dropped. And if the Receiver is dropped first, send fails, so the sending task knows nobody is listening and can stop.

  40. What does tokio::select! do?

    Waits on several futures at once, runs the branch of whichever finishes first, and drops the rest. Chatterbox races the next piece of an answer against Ctrl-C.

  41. Rust error E0425: this name does not exist here. What does it mean, and what is the usual fix?

    Rust cannot find anything with that name at this spot in your program. A name is the label you give a value with let, like bot_name. Usually it is a typo, or a name made inside curly braces { } that have already closed: a name only lives until the closing brace of the block it was made in. Fix: Compare the spelling letter by letter with the let line; the compiler often suggests the name you meant. If the name was made inside a block, make it before the block instead.

  42. Rust error E0433: the compiler does not know where this comes from. What does it mean, and what is the usual fix?

    Some ready-made tools, like HashMap (a lookup table that finds a value by its key), live in Rust's standard library but are not switched on automatically. You have to say where to find them with a use line at the top of the file. Until you do, Rust does not recognise the name. Fix: Add use std::collections::HashMap; at the top of the file. The help line under the error usually prints the exact use line to copy.

  43. Rust error E0061: wrong number of inputs to a function. What does it mean, and what is the usual fix?

    A function lists the inputs it needs, called arguments, in the brackets after its name. Every call must give exactly that many, in the same order. Here reply asks for two, the message and the bot's name, but the call gives only one. Fix: Put the call next to the fn line and count. Add the missing argument, reply("hi", "Chatterbox"), or remove the extra one. The compiler points at the argument you left out.

  44. Rust error E0616: this field is private. What does it mean, and what is the usual fix?

    A module is a named section of your code, often its own file, like message.rs. Inside a module everything is private by default: code outside it cannot touch a field unless the field is marked pub (short for public). Here main sits outside the message module and reaches for text, which the module kept to itself. Fix: Either mark the field pub, if outside code may read and change it freely, or (usually better) add a pub fn method that hands it out, like msg.text(), so the module stays in charge of its own data.

  45. Rust error E0599: this type has no method with that name. What does it mean, and what is the usual fix?

    A method is a function that belongs to a type and is called with a dot, like history.push(msg). The type you called it on has no method with that name, usually because the name is a near-miss guess. A Vec (Rust's list that can grow) adds an item with push, not add. Fix: Read the help line: the compiler often names the method you meant. In your editor, type the dot and wait for the list of real methods. If the value is an Option or Result (a wrapper that may or may not hold a value), take the value out first.

  46. Rust error E0308: the value is the wrong type. What does it mean, and what is the usual fix?

    Every value in Rust has a type: a whole number, a piece of text, true or false, and so on. When a name is meant to hold one type and you give it another, Rust stops; it never quietly turns one into the other. Here "3" in quotes is text, not the number 3. The message lists what it expected and what it found. Fix: Read the expected / found pair. Give a value of the right type (3 without quotes), change the type you wrote, or convert on purpose: "3".parse().unwrap() turns text into a number.

  47. Rust error E0282: the compiler cannot tell what type this is. What does it mean, and what is the usual fix?

    Rust usually works out a value's type by itself from how you use it. Here history is an empty list and nothing is ever put in it, so Rust cannot tell whether it is a list of numbers, of text, or of something else. It needs one hint. Fix: Say the type once: let history: Vec<String> = Vec::new();. Putting an item in later, like history.push(msg), also gives Rust the hint.

  48. Rust error E0063: a struct is missing a field. What does it mean, and what is the usual fix?

    A struct bundles several named values, called fields, into one, like a Message with an author and a text. When you build one you must fill in every field; there is no leaving one blank. If a field may really have nothing in it, its type is Option (either Some(value) or None) and you write None. Fix: Add the field the error names: text: String::from("hello").

  49. Rust error E0004: a match does not handle every case. What does it mean, and what is the usual fix?

    An enum is a type that is exactly one of a fixed list of options, like a Command that is Help, Quit or History. A match must say what to do for every option, and this one forgot History. That check is what makes adding a new command safe: Rust lists every place that still needs updating. Fix: Add an arm for the missing option: Command::History => …. If you really mean "everything else", end with a _ => … arm.

  50. Rust error E0369: two pieces of quoted text cannot be joined with +. What does it mean, and what is the usual fix?

    Text in quotes, like "Welcome", is a fixed piece of text (its type is &str) that cannot grow. + only works when the left side is a String, Rust's growable text, because the right side is added onto its end. Two fixed pieces have nothing to grow, so + is not allowed between them. Fix: Use format!("{}{}", a, b), which builds a new String, or make the left side a String first: String::from("Welcome to ") + "Chatterbox".

  51. Rust error E0072: a type that contains itself is endlessly big. What does it mean, and what is the usual fix?

    Rust needs to know how much memory each type takes. A Message that directly holds another Message, which holds another, and so on, would be endlessly big. The course does not cover types that contain themselves; the idea is to keep the inner one in a Box (a small, fixed-size pointer to a value stored elsewhere), which the traits lesson uses. Fix: Wrap the field in a box: reply_to: Option<Box<Message>>.

  52. Rust error E0384: changing a value you did not mark as changeable. What does it mean, and what is the usual fix?

    A name made with let keeps the same value forever, unless you say up front that it may change. That makes it easy to see which values move around in a program. Here count was made without that promise and then given a new value. Fix: Add mut (short for mutable, meaning changeable) where the name is made: let mut count = 0;.

  53. Rust error E0596: calling a changing method on a value that is not mut. What does it mean, and what is the usual fix?

    Some methods change the value they are called on: push adds an item, so the list changes. You may only call those on a value whose name was made with let mut. The message says "borrow as mutable": to borrow is to use a value without taking it over, and mutable means with permission to change it. Fix: Make the name changeable: let mut history = ….

  54. Rust error E0594: changing a field of a value that is not mut. What does it mean, and what is the usual fix?

    A struct's fields are the named values inside it, like the text of a Message. If the name holding the struct was made without mut, none of its fields can change either. It is the same rule as changing a plain value, one level down. Fix: Make the whole value changeable: let mut msg = Message { … };.

  55. Rust error E0382: using a value after you handed it away. What does it mean, and what is the usual fix?

    Every value in Rust has exactly one owner; think of the owner as the one holder of its key. When you pass msg into a function, or assign it to another name, the key goes with it. That is called a move, and after it the old name holds nothing, so using it again is an error. Fix: Lend the value instead of handing it over: make the function take &str and call send(&msg). The & means "borrow it and give it back". If you really need two separate copies, use msg.clone().

  56. Rust error E0505: handing a value away while it is lent out. What does it mean, and what is the usual fix?

    To borrow a value is to use it through a reference, written with &, without owning it. While a borrow will still be used, the owner cannot give the value away, or the borrower would be left holding a key to nothing. Here preview borrows msg, then msg moves to sent, then preview is used. Fix: Finish with the borrow before the move: print preview first, then move msg. Or borrow again instead of moving.

  57. Rust error E0507: taking a piece out of something you only borrowed. What does it mean, and what is the usual fix?

    The & in &Message means the function only borrowed the message; it does not own it. Returning msg.text would carry the text away for good and leave the real owner with a message missing its text. A borrower may look at the pieces but cannot take them home. Fix: Hand back a borrowed view instead (-> &str and &msg.text), return a copy with msg.text.clone(), or have the function take the whole Message without the &.

  58. Rust error E0381: reading a name before it has a value. What does it mean, and what is the usual fix?

    You may make a name now and give it a value later, but it must have a value before anything reads it; Rust has no automatic "empty" or zero. Here banner only gets a value when quiet is false. On the other path it would be read while it still holds nothing. Fix: Give it a value on every path by adding an else branch, or give it one right where it is made: let banner = if quiet { "" } else { "Welcome!" };.

  59. Rust error E0502: changing something while it is still being read. What does it mean, and what is the usual fix?

    first borrows the first message in history: it looks at it through a reference without owning it. Then push tries to change the list while that borrow is still going to be used. Growing a list can move its items to a new place in memory, leaving first pointing at nothing, so Rust allows many readers or one changer, never both at once. Fix: Use first before you push, or take your own copy with history[0].clone() so nothing is borrowed while the list changes.

  60. Rust error E0499: two places changing the same value at once. What does it mean, and what is the usual fix?

    &mut is a borrow that is allowed to change the value. Only one of those may exist at a time, so two parts of a program can never change the same thing at the same moment and trip over each other. Here a and b both hold a changing borrow of history, and both are still used. Fix: Finish with the first &mut before taking the second: move a.push(…) above the line that makes b. Often you need no extra names at all; call history.push(…) directly.

  61. Rust error E0277: the type is missing an ability this needs. What does it mean, and what is the usual fix?

    A trait is a named ability a type can have, such as being printable or comparable. This error means a value was used somewhere that needs an ability its type lacks. Beginners meet it most often by using ? (which passes an error up to whoever called the function) inside a main that does not return a Result, so there is nowhere for the error to go. Fix: Read the line naming which trait is not implemented for which type. For ? in main, make main return a Result and end it with Ok(()), as below. For an ability missing from your own struct, add it, often with #[derive(…)].

  62. Rust error E0046: an impl is missing a method its trait requires. What does it mean, and what is the usual fix?

    A trait is a list of methods a type promises to have; Chatterbox's Replier trait promises a reply method. An impl Replier for Echo block is where Echo keeps that promise, and it must write every method the trait lists. Here the block is empty. Fix: Write each missing method inside the impl block. The error names them and shows the exact first line to copy.

  63. Rust error E0038: this trait cannot be used with dyn. What does it mean, and what is the usual fix?

    dyn Replier means "some type with the Replier ability, chosen while the program runs". That only works if every method can be called without knowing the exact type, and a method that returns Self (the exact type) cannot be. The course uses dyn in the traits lesson but does not cover this rule; the idea is that such a trait still works with generics, just not behind dyn. Fix: Remove the method that returns Self, add where Self: Sized to it so dyn leaves it out, or use generics instead of dyn.

  64. Rust error E0525: a closure that can run only once was asked to run again. What does it mean, and what is the usual fix?

    A closure is a small unnamed function written on the spot, like || println!("hi"). If its body gives away (moves) a value it captured, it can only run once, because after the first run that value is gone. The course does not cover the kinds of closure (Fn, FnOnce); here run_twice asks for one it can call again and again, and this one gives banner away. Fix: Inside the closure, borrow the value instead of moving it: println!("{banner}") only reads it. Or clone it. If one call really is enough, the function can ask for FnOnce instead.

  65. Rust error E0597: the borrowed value is gone before the borrow is. What does it mean, and what is the usual fix?

    A value is cleaned up at the closing brace } of the block it was made in. Here preview borrows msg, but msg is cleaned up first, so preview would point at nothing. The course does not cover lifetimes yet; a lifetime is simply how long a value stays alive, and a borrow must never outlive its value. Fix: Make the owner live longer by making msg outside the inner block, or keep an owned copy (a String) instead of a borrow.

  66. Rust error E0106: a returned borrow must say which input it comes from. What does it mean, and what is the usual fix?

    This function hands back a borrow, but it was given two borrowed inputs, and Rust will not guess which one the result comes from. The course does not cover lifetimes yet; a lifetime is how long a value stays alive, and you name one with a short label like 'a to tie the result to its inputs. Fix: Add a lifetime label: fn longer<'a>(a: &'a str, b: &'a str) -> &'a str. Or return an owned String, so nothing is borrowed.

  67. Rust error E0515: returning a borrow of something the function throws away. What does it mean, and what is the usual fix?

    Values made inside a function are cleaned up when the function ends. Returning a borrow of one would hand the caller a reference to something already gone. The course does not cover lifetimes yet; the rule here is simply that a borrow cannot outlive the value it points at. Fix: Return the value itself: change the return type to String and return loud.

  68. Rust error E0716: a value with no name is cleaned up while still borrowed. What does it mean, and what is the usual fix?

    String::from("Welcome") here makes a value with no name, and Rust cleans such a value up at the end of the line. banner keeps a borrow of it and is used on the next line, after it is gone. The course does not cover lifetimes yet; the fix is to give the value a name so it lives long enough. Fix: Store the value in its own variable first: let text = String::from("Welcome"); then let banner = text.as_str();.

  69. Rust error E0728: .await used outside async code. What does it mean, and what is the usual fix?

    .await means "pause here until this is ready, and let other work run meanwhile". Only async code can pause like that: a function marked async fn, or a block marked async { }. A plain function, including a plain main, runs straight through and has nowhere to pause. Chapter 10 covers this. Fix: Mark the function async. For main, a runtime has to drive it: with tokio, write #[tokio::main] above async fn main().

  70. Rust error E0373: a thread or task may outlive the values it borrows. What does it mean, and what is the usual fix?

    A thread, or a task started with tokio::spawn, is a second line of work that runs at the same time as the rest of your program and may keep running after the function that started it has returned. Rust cannot be sure it finishes before the values it borrows are cleaned up, so it will not let it merely borrow them: it must own what it uses. Lesson 10.2 meets this. Fix: Put move before the closure, thread::spawn(move || …), or before the block, tokio::spawn(async move { … }), so the new work takes ownership of history. Clone first if you still need it here.