You can look things up here, just as you would while writing code.
These questions revisit ownership and borrowing, Option and Result,
lifetimes, traits, enums, and modules, then ask you to predict behavior in the
CSV and environment-file projects. The Rc question includes the
shared-ownership rules you need; you don't need to have finished the
smart-pointer chapter. Pick an answer to lock it in and reveal the explanation
for every choice, not just the right one. The explanations for the other answers
are worth reading too, especially when you were choosing between two. Use Tab to
move between answers and Enter or Space to choose one. After answering every
question, you can reset the quiz and try again.
You won't get a grade, there's no time limit, and the course doesn't keep a record of your answers.
What happens to s when you call consume(s) with fn consume(s: String)?
Think about ownership rules.
&String (or &str).
Bare String means a move.String does not implement Copy. This call moves it; use s.clone() if you need a separate copy of its bytes.String by value. Method calls can automatically borrow their receiver, but this is an ordinary function call.String by value transfers ownership; you can't use it in the caller again unless the function returns it.Which of these creates a mutable variable in Rust?
const is for compile-time constants and is never mutable.mut keyword opts a binding into mutability.var keyword.What is Option<T> for?
Think about what other languages use null for.
Result<T, E> is for.
None doesn't tell you why a value is absent.Some(value) when the value is present and None when it isn't, without needing null.as, From/Into, or try_from.Given let value = String::from("hello");, how do you borrow this String immutably?
& operator produces a shared, immutable reference.* dereferences a reference; it doesn't create one.String::clone() copies the string bytes into another owned String. This is specific to String; cloning a shared pointer can keep sharing its data.What happens when this inner scope ends?
let name = String::from("Ada");
{
let moved = name;
}
// Can we use name here?
let borrowed = &name;) if you want name to keep owning the string.name has been moved from. Shared ownership is possible with types such as Rc, a reference-counted pointer, but this code uses a plain String.String. let copied = name.clone(); would create a separate string.String into moved. At the end of the inner scope, moved drops it and frees its buffer.What's the difference between String and &str?
"...") are baked into the binary as &'static str, but String values are very much runtime.String mutable with let mut, but it can also be immutable; the distinction here is ownership.String owns its bytes; an &str borrows bytes from somewhere else, such as a String or a literal.What does #[derive(Debug)] do?
Debug is implemented, you can format the value with {:?} or {:#?}.#[warn(...)], #[deny(...)]).What does the ? operator do on a Result<T, E>?
It collapses a very common match pattern.
? returns early, it doesn't unwind.? handles Ok and Err here, not null.match res { Ok(v) => v, Err(e) => return Err(e.into()) }..ok(), .err()), but ? is not one of them.Which of these creates an empty Vec?
vec! macro with no elements gives you an empty vector.Vec, not Vector.new keyword.
Constructors are just associated functions: Vec::new().What happens if you call HashMap::get for a key that doesn't exist?
Rust prefers types over panics for expected absence.
entry().or_default() is opt-in.)Option<&V>, so you can handle a missing key through the API.entry API, not the default behaviour of get.get never panics.
Indexing (map["key"]) does, but get returns an Option.How do you create an owned String from a string literal?
to_string(), from the ToString trait.to_owned() clones the &str into a heap-allocated String.
String::from("hello") and "hello".to_string() also work.String isn't callable like that.
Use String::from("hello").new keyword in Rust.Which of these is true of Rust enums?
Rust enums are closer to algebraic data types than to C-style enums.
Option, Result, and many parser ASTs are enums.Enum trait.match instead, and == only works if the enum implements PartialEq.longest returns a reference to whichever input string is longer. Its signature is:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str
Will this code compile? Why or why not?
let first = String::from("a rainy afternoon");
let result;
{
let second = String::from("sun");
result = longest(&first, &second);
}
println!("{result}");
Which string is dropped at the closing brace? The compiler checks the call using the function's signature, not by comparing the lengths of these strings.
'a connects the returned reference to both inputs. We can use the result only while both strings are still alive. Moving the println! inside the block would make this compile.first is longer, but the compiler doesn't use that fact to check the borrow. The signature allows the result to borrow from either input, so both strings must still be alive when we use it.second is still dropped at the closing brace, before the println!.first can live longer than second, as it does here.Compare these two clones:
use std::rc::Rc;
let text = String::from("hello");
let text_copy = text.clone();
let shared = Rc::new(String::from("hello"));
let shared_copy = Rc::clone(&shared);
Rc<T> is a reference-counted pointer: several handles can own the same allocation.
Cloning an Rc adds a handle, and the contained value is dropped when the last strong handle is dropped.
Which pair still shares the same string allocation?
String::clone copies the bytes into a separate buffer. Rc::clone increments a reference count without cloning the contained String. Clone does not promise a deep copy.Clone implementation. A new Rc handle still points to the same data; a new String owns copied bytes.text_copy would not change text. The two Rc handles share instead.Rc pair shares its allocation, but cloning this nonempty String creates another buffer.What is match primarily for?
String/str methods, not in control flow.match on the variants.regex crate.
Not built into the language.In { let s = String::from("hello"); }, when is s dropped?
Here, s is neither moved nor explicitly dropped before the closing brace.
String can't be set to null.Drop::drop directly.
Use std::mem::drop if you need to drop early.drop() is one way to force early dropping, but normal end-of-scope drops happen automatically.s still owns the string when its scope ends, so its buffer is freed. Dropping one Rc handle would not drop shared data while another strong handle remains.What does 'static mean in the reference type &'static str?
'static says nothing about where the memory lives, only how long.T: 'static does not mean an owned value must live forever.'static is about lifetime, not mutability.
A static mut is 'static and mutable (but unsafe to touch).Sized trait, which is unrelated.What is a trait in Rust?
Send, Sync, Copy, …) are a kind of trait, but traits in general aren't about memory management.How do you make an item visible from outside its module?
statics still default to private; they need pub static to escape the module.global keyword.pub makes the item visible across modules; you can also limit its visibility with pub(crate) or pub(super).extern is for FFI bindings, not module visibility.Given let mut x: &i32 = &42;, which line binds r to an i32 rather than a reference?
Track the type after each operator. Does the whole expression produce an integer, or borrow one again?
x is an &i32, so *x reads the integer. Since i32 implements Copy, r receives a copy of 42.*x refers to the integer, then & borrows it again. The type of r is &i32.In the CSV project, a comma inside quotes is data, and "" inside a quoted field encodes one literal ".
Predict the fields for this row:
"a""b,c",d
Trace whether the parser is inside quotes after consuming the escaped pair.
d.d belongs in a second field.The environment-file project's typed lookup returns Option<T>. For a u16 port, its body could be:
values.get(key).and_then(|raw| raw.parse::<u16>().ok())
Predict the results when PORT is absent, is "8080", and is "eight", in that order.
and_then calls the closure only for Some. .ok() converts a successful Result to Some and an error to None.
Some(8080). .ok() discards the parse error for eight, so this API cannot distinguish invalid text from an absent key.None; the caller can choose a default separately.unwrap here. Parsing invalid text returns an error, which .ok() converts to None without panicking..ok() turns it into None. To preserve that distinction, you would need a different return type, such as Result<Option<u16>, ParseIntError>.Extra practice to explore at your own pace. These chapters do not count toward course progress.