Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

📝 Knowledge Test

/
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

target20:00

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.


Q1
Concept Match

Match the Project Hierarchy

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

Package
drag a definition here…
Binary Crate
drag a definition here…
Library Crate
drag a definition here…
Module
drag a definition here…

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.
Q2
Quiz
Select 0/2

Regarding the structure of a standard Rust package, which of the following statements are TRUE?

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.
Q3
Code Cloze
Rust

Managing Visibility and Scopes

Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.

1mod 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
13fn main() {
14 // Bring the struct into the local scope
15 ····· crate::sensors::Data;
16}

Available Snippets

use
super
pub
extern
private
mod
self
Q4
Quiz
Select 0/2

In a professional Rust codebase, why is it considered a best practice to keep module contents private by default?

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.
Q5
Quiz
Select 0/2

Which of the following statements correctly contrast the safety of indexing (data[i]) versus the .get(i) method for a Vec<T>?

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.
Q6
Concept Match

Match the Collection Characteristics

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

Vec<T>
drag a definition here…
String
drag a definition here…
&str
drag a definition here…
HashMap<K, V>
drag a definition here…

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.
Q7
Code Output
Rust

Predict the output: HashMap Entry API

Read the code below, then choose the terminal output it produces and click Submit.

1use std::collections::HashMap;
2
3fn 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}
stdout
10 20
stdout
0 20
stdout
10 0
stdout
0 0
Q8
Quiz
Select 0/2

Why is it often dangerous to perform a byte-level slice (e.g., &s[0..3]) on a Rust String containing international characters?

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.
Q9
Quiz
Select 0/2

In resource-constrained edge programming, what is a primary advantage of 'Arena Allocation'?

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.
Q10
Quiz
Select 0/2

In Rust's error handling philosophy, when is it appropriate to use the panic! macro instead of Result<T, E>?

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.
Q11
Code Cloze
Rust

Error Propagation with the ? Operator

Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.

1use std::fs::File;
2use std::io::{self, Read};
3
4fn 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}

Available Snippets

?
Ok
Err
unwrap
?
!
return
Q12
Concept Match

Match Error Handling Methods

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

unwrap()
drag a definition here…
expect(msg)
drag a definition here…
unwrap_or(def)
drag a definition here…
Result<T, E>
drag a definition here…

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.
Q13
Quiz
Select 0/2

Why is Rust's Option<T> type considered superior to the traditional null pointer (nullptr) used in C++?

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.
Q14
Quiz
Select 0/2

What are the primary performance characteristics of 'Static Dispatch' (Monomorphisation) in Rust?

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.
Q15
Concept Match

Match the Standard Library Traits

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

Clone
drag a definition here…
Debug
drag a definition here…
PartialOrd
drag a definition here…
Default
drag a definition here…

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).
Q16
Code Cloze
Rust

Generic Structs and the Turbofish

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
2struct Point<T> {
3 x: T,
4 y: T,
5}
6
7fn 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}

Available Snippets

T
u32
int
float
i32
number
Q17
Code Order
Rust

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.
trait Summary {
fn summarize(&self) -> String {
}
impl Summary for Tweet {
fn summarize(&self) -> String;
}
format!("Tweet: {}", self.content)
}

Part 5: Closures, Iterators, and Lifetimes

Section titled “Part 5: Closures, Iterators, and Lifetimes”
Q18
Quiz
Select 0/2

How does Rust distinguish between the three closure traits: Fn, FnMut, and FnOnce?

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.
Q19
Code Cloze
Rust

Moving Environment into Closures

Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.

1fn 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
Q20
Concept Match

Match the Iterator Adaptors

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

map()
drag a definition here…
filter()
drag a definition here…
fold()
drag a definition here…
collect()
drag a definition here…

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.
Q21
Quiz
Select 0/2

What does the lifetime annotation <'a> signify when used in a function signature like longest<'a>(x: &'a str, y: &'a str) -> &'a str?

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.

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.