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 by value transfers ownership; you can't use it in the caller again unless the function returns it.String does not implement Copy. This call moves it; use s.clone() if you need a separate copy of its bytes.&String (or &str).
Bare String means a move.String by value. Method calls can automatically borrow their receiver, but this is an ordinary function call.Which of these creates a mutable variable in Rust?
var keyword.const is for compile-time constants and is never mutable.mut keyword opts a binding into mutability.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?
* dereferences a reference; it doesn't create one.& operator produces a shared, immutable reference.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?
String into moved. At the end of the inner scope, moved drops it and frees its buffer.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.let borrowed = &name;) if you want name to keep owning the string.String. let copied = name.clone(); would create a separate string.What's the difference between String and &str?
String owns its bytes; an &str borrows bytes from somewhere else, such as a String or a literal."...") 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.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.
.ok(), .err()), but ? is not one of them.? handles Ok and Err here, not null.match res { Ok(v) => v, Err(e) => return Err(e.into()) }.? returns early, it doesn't unwind.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 API, not the default behaviour of get.get never panics.
Indexing (map["key"]) does, but get returns an Option.Option<&V>, so you can handle a missing key through the API.entry().or_default() is opt-in.)How do you create an owned String from a string literal?
to_owned() clones the &str into a heap-allocated String.
String::from("hello") and "hello".to_string() also work.to_string(), from the ToString trait.new keyword in Rust.String isn't callable like that.
Use String::from("hello").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 can live longer than second, as it does here.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!.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.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.Clone implementation. A new Rc handle still points to the same data; a new String owns copied bytes.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() 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.Drop::drop directly.
Use std::mem::drop if you need to drop early.What does 'static mean in the reference type &'static str?
'static is about lifetime, not mutability.
A static mut is 'static and mutable (but unsafe to touch).Sized trait, which is unrelated.'static says nothing about where the memory lives, only how long.T: 'static does not mean an owned value must live forever.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?
extern is for FFI bindings, not module visibility.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).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 belongs in a second field.d.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.
.ok() turns it into None. To preserve that distinction, you would need a different return type, such as Result<Option<u16>, ParseIntError>.Some(8080). .ok() discards the parse error for eight, so this API cannot distinguish invalid text from an absent key.unwrap here. Parsing invalid text returns an error, which .ok() converts to None without panicking.None; the caller can choose a default separately.Extra practice to explore at your own pace. These chapters do not count toward course progress.