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.
An internal organisational tool used to partition code and manage visibility within a crate.
A unit of compilation that provides reusable code but lacks a standalone entry point.
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.
The presence of a Cargo.toml file in the root directory is what defines the directory as a package.
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. 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
mod
extern
super
self
private
pub
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 satisfy the 'orphan rule', which forbids public functions from calling private helper methods.
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. 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.
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.
Indexing will cause the program to panic immediately if the requested index exceeds the current length. 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
An owned, mutable, and UTF-8 encoded buffer that manages character data on the heap.
A non-owning, 16-byte fat pointer that provides an immutable view into string data.
A collection storing unique keys and associated values with amortised O(1) lookup.
A heap-allocated, growable array providing fast random access to contiguous elements.
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 Rust requires all string slices to be an even number of bytes to maintain memory alignment.
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 slicing at an invalid character boundary will cause the program to panic at runtime. Submit Answer
In resource-constrained edge programming, what is a primary advantage of 'Arena Allocation'? derekmolloy.ie
It allows all objects in a region to be deallocated simultaneously, preventing fragmentation.
It allows the program to automatically resize the stack at runtime to prevent overflows.
It provides deterministic performance by using a simple 'pointer bump' for allocations.
It eliminates the need for the 'Drop' trait by leaving memory permanently allocated. 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 the program enters an unrecoverable, inconsistent state due to a fundamental logic bug.
When a network timeout occurs while attempting to fetch data from a remote edge sensor.
When a critical invariant, such as an array index being within bounds, is unexpectedly violated.
When a user provides a malformed string that fails to parse into a valid integer value. 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, or falls back to a provided default value if it is Err.
An enum representing either successful computation (Ok) or a recoverable failure (Err).
Returns the value inside Ok, but panics with a default message if the result is Err.
Returns the value inside Ok, but panics with a custom diagnostic message 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 eliminates the possibility of null-dereference errors by making the presence of a value a type-level check.
Because it forces developers to explicitly handle the 'None' case before accessing the underlying data.
Because it uses significantly less memory on the stack by compressing the value and its null-tag into 4 bytes.
Because it guarantees that the memory occupied by the absent value is automatically zeroed out at runtime. Submit Answer
What are the primary performance characteristics of 'Static Dispatch' (Monomorphisation) in Rust? derekmolloy.ie
It involves a minor runtime overhead due to the requirement of looking up method addresses in a vtable.
It allows the compiler to generate specialised, type-specific code, enabling aggressive optimisations like inlining.
It leads to 'zero runtime cost' abstractions at the expense of potentially larger compiled binary sizes.
It permits a single compiled function to handle multiple types at runtime without needing to be recompiled. 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;
format!("Tweet: {}", self.content)
fn summarize(&self) -> String {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.
FnOnce can only be called exactly once because the first call consumes or moves the captured 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. 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
data
static
move
async
ref
val
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.
Retains only those elements that satisfy a specific Boolean predicate.
Reduces the entire sequence into a single value using an accumulator and a closure.
Consumes the lazy iterator and gathers the resulting elements into a collection.
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 forces the strings to be copied into a temporary local buffer to prevent any potential memory corruption.
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. 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.