Knowledge Test Score Board
— · 0%
Work through the questions below — your score updates as you go.
Keep going · grade bands mapped to DCU's honours scale
Only your first attempt at each question counts toward this score. Reload the page to reset.
This section contains 21 interactive knowledge checks designed to test your understanding of the materials covered in Chapter 8. The questions cover Rust’s module system, dynamic collections, explicit error handling, and advanced abstraction mechanisms like traits and closures.
Please remember that many of the quizzes have multiple correct answers , and you must select all applicable options to succeed. I have carefully balanced the lengths of the answers, so take your time to evaluate each option thoroughly.
Good luck!
Derek.
Concept Match
Match the Project Hierarchy derekmolloy.ie Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.
Definition Pool
A bundle of one or more crates, uniquely defined by its Cargo.toml manifest.
An executable program that must contain a main() function as its entry point.
A unit of compilation that provides reusable code but lacks a standalone entry point.
An internal organisational tool used to partition code and manage visibility within a crate.
Submit
Regarding the structure of a standard Rust package, which of the following statements are TRUE? derekmolloy.ie
Every binary crate within a package is required to share the same version number as the library.
A package may contain at most one library crate but can support multiple binary crates.
A single package is restricted to containing exactly one library crate and zero binary crates.
The presence of a Cargo.toml file in the root directory is what defines the directory as a package. Submit Answer
Managing Visibility and Scopes derekmolloy.ie Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.
1 mod sensors {
2 // Visible only within the current crate
3 ····· (crate) struct Data {
4 pub value: f32,
5 }
6
7 // Visible only to the parent module
8 pub( ····· ) fn internal_log() {
9 println!("Logging...");
10 }
11 }
12
13 fn main() {
14 // Bring the struct into the local scope
15 ····· crate::sensors::Data;
16 }
Available Snippets
use
super
pub
extern
private
mod
self
Submit
In a professional Rust codebase, why is it considered a best practice to keep module contents private by default? derekmolloy.ie
To minimise the public API surface, reducing the risk of breaking changes for downstream consumers.
To prevent the compiler from generating duplicate machine code for internal helper functions.
To enforce strict encapsulation, ensuring that internal implementation details are hidden from users.
To satisfy the 'orphan rule', which forbids public functions from calling private helper methods. Submit Answer
Which of the following statements correctly contrast the safety of indexing (data[i]) versus the .get(i) method for a Vec<T>? derekmolloy.ie
Indexing is preferred in edge programming because it returns a safe default value if the index is out of bounds.
Indexing will cause the program to panic immediately if the requested index exceeds the current length.
The .get() method is considered 'unsafe' code and requires a dedicated unsafe block to execute correctly.
The .get() method returns an Option<&T>, forcing the developer to handle the potential absence of a value. Submit Answer
Concept Match
Match the Collection Characteristics derekmolloy.ie Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.
Definition Pool
A non-owning, 16-byte fat pointer that provides an immutable view into string data.
A heap-allocated, growable array providing fast random access to contiguous elements.
An owned, mutable, and UTF-8 encoded buffer that manages character data on the heap.
A collection storing unique keys and associated values with amortised O(1) lookup.
Submit
Predict the output: HashMap Entry API derekmolloy.ie Read the code below, then choose the terminal output it produces and click Submit.
1 use std::collections::HashMap;
2
3 fn main() {
4 let mut counts = HashMap::new();
5 counts.insert("A", 10);
6
7 // Attempt to insert or update
8 counts.entry("A").or_insert(0);
9 counts.entry("B").or_insert(20);
10
11 println!("{} {}", counts["A"], counts["B"]);
12 }
Submit Answer
Why is it often dangerous to perform a byte-level slice (e.g., &s[0..3]) on a Rust String containing international characters? derekmolloy.ie
Because slicing at an invalid character boundary will cause the program to panic at runtime.
Because the String type automatically recompiles to ASCII when sliced to save on stack memory.
Because UTF-8 characters can vary in byte-width, and slicing may occur in the middle of a character.
Because Rust requires all string slices to be an even number of bytes to maintain memory alignment. Submit Answer
In resource-constrained edge programming, what is a primary advantage of 'Arena Allocation'? derekmolloy.ie
It provides deterministic performance by using a simple 'pointer bump' for allocations.
It allows the program to automatically resize the stack at runtime to prevent overflows.
It eliminates the need for the 'Drop' trait by leaving memory permanently allocated.
It allows all objects in a region to be deallocated simultaneously, preventing fragmentation. Submit Answer
In Rust's error handling philosophy, when is it appropriate to use the panic! macro instead of Result<T, E>? derekmolloy.ie
When a critical invariant, such as an array index being within bounds, is unexpectedly violated.
When the program enters an unrecoverable, inconsistent state due to a fundamental logic bug.
When a user provides a malformed string that fails to parse into a valid integer value.
When a network timeout occurs while attempting to fetch data from a remote edge sensor. Submit Answer
Error Propagation with the ? Operator derekmolloy.ie Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.
1 use std::fs::File;
2 use std::io::{self, Read};
3
4 fn get_config() -> Result<String, io::Error> {
5 // Open the file or return the error early
6 let mut f = File::open("config.txt") ····· ;
7 let mut s = String::new();
8 // Read the contents or return the error early
9 f.read_to_string(&mut s) ····· ;
10 // Return the successful result
11 ····· (s)
12 }
Submit
Concept Match
Match Error Handling Methods derekmolloy.ie Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.
Definition Pool
Returns the value inside Ok, but panics with a custom diagnostic message if it is Err.
Returns the value inside Ok, but panics with a default message if the result is Err.
An enum representing either successful computation (Ok) or a recoverable failure (Err).
Returns the value inside Ok, or falls back to a provided default value if it is Err.
Submit
Why is Rust's Option<T> type considered superior to the traditional null pointer (nullptr) used in C++? derekmolloy.ie
Because it uses significantly less memory on the stack by compressing the value and its null-tag into 4 bytes.
Because it eliminates the possibility of null-dereference errors by making the presence of a value a type-level check.
Because it guarantees that the memory occupied by the absent value is automatically zeroed out at runtime.
Because it forces developers to explicitly handle the 'None' case before accessing the underlying data. Submit Answer
What are the primary performance characteristics of 'Static Dispatch' (Monomorphisation) in Rust? derekmolloy.ie
It allows the compiler to generate specialised, type-specific code, enabling aggressive optimisations like inlining.
It involves a minor runtime overhead due to the requirement of looking up method addresses in a vtable.
It permits a single compiled function to handle multiple types at runtime without needing to be recompiled.
It leads to 'zero runtime cost' abstractions at the expense of potentially larger compiled binary sizes. Submit Answer
Concept Match
Match the Standard Library Traits derekmolloy.ie Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.
Definition Pool
Enables explicit deep copying of heap-allocated resources (e.g., String).
Provides the logic required for comparison operators like <, >, <=, and >=.
Allows a type to be formatted as developer-friendly output using the {:?} placeholder.
Supplies a standard 'empty' or initial value for a type (e.g., 0 for integers).
Submit
Generic Structs and the Turbofish derekmolloy.ie Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.
1 // Define a generic point that works with any type T
2 struct Point<T> {
3 x: T,
4 y: T,
5 }
6
7 fn main() {
8 // Explicitly specify the type using the Turbofish
9 let p = Point::< ····· > { x: 5, y: 10 };
10
11 // Parse a string into a specific numeric type
12 let age = "25".parse::< ····· >().unwrap();
13 }
Submit
Implementing a Custom Trait Drag the tiles to arrange the code in the correct order, then click Submit. Locked lines stay in place. Indentation is dynamically applied based on the location of braces.
fn summarize(&self) -> String {
fn summarize(&self) -> String;
format!("Tweet: {}", self.content)Submit Order
How does Rust distinguish between the three closure traits: Fn, FnMut, and FnOnce? derekmolloy.ie
Fn borrows variables immutably; FnMut borrows variables mutably; FnOnce takes ownership of variables.
Fn closures can only be called in a single-threaded environment, while FnOnce is required for multi-threading.
Fn is the only trait that allows a closure to be returned from a function using the 'impl' keyword.
FnOnce can only be called exactly once because the first call consumes or moves the captured variables. Submit Answer
Moving Environment into Closures derekmolloy.ie Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.
1 fn main() {
2 let data = vec![1, 2, 3];
3
4 // Force the closure to take ownership of its environment
5 let handle = std::thread::spawn( ····· || {
6 println!("Vector from thread: {:?}", ····· );
7 });
8
9 handle.join().unwrap();
10 }
Available Snippets
ref
static
async
val
data
move
Submit
Concept Match
Match the Iterator Adaptors derekmolloy.ie Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.
Definition Pool
Transforms each element into something else using a provided closure.
Consumes the lazy iterator and gathers the resulting elements into a collection.
Reduces the entire sequence into a single value using an accumulator and a closure.
Retains only those elements that satisfy a specific Boolean predicate.
Submit
What does the lifetime annotation <'a> signify when used in a function signature like longest<'a>(x: &'a str, y: &'a str) -> &'a str? derekmolloy.ie
It provides the borrow checker with information to statically verify that the output doesn't outlive its source.
It indicates that the returned reference will be valid for as long as the shortest of the two input lifetimes.
It instructs the compiler to allocate the input strings on the static heap for the entire duration of the program.
It forces the strings to be copied into a temporary local buffer to prevent any potential memory corruption. Submit Answer
Congratulations on completing the Chapter 8 Knowledge Test!
Review any questions you missed to ensure a solid grasp of Rust modules, collections, and its powerful abstraction mechanisms before moving forward.