We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Trait Objects and Dispatch step 10 of 10
Choose parameter types and shrink an enum
Reduce the size of Rule by boxing its large Table payload. Update the
corresponding constructor in rule_report, keep the supplied function
signatures, and preserve the behavior below. Do not suppress the lint or
remove the const _: signature checks.
The starter compiles with rustc, but fails Clippy's large_enum_variant
check and the harness's requirement that size_of::<Rule>() <= 32 bytes.
The size limit is an exercise check on the grading target, not a portable
guarantee about Rust enum layout.
Required behavior
pub fn normalise(entries: Vec<String>, banned: Vec<String>) -> Vec<String>
pub fn describe(rule: &Rule) -> String
pub fn rule_report() -> Vec<String>
normalise trims both ends of each entry with str::trim, then lowercases
it with str::to_lowercase. Apply the same transformation to banned words.
Drop empty results, exact matches in the transformed banned list, and later
duplicates. Preserve the order of the first surviving occurrences.
For example, entries [" Alpha ", "beta", "ALPHA", "gamma"] and banned
["Gamma"] produce ["alpha", "beta"]. An empty entries vector produces an empty result.
Duplicate or empty banned words have no additional effect.
Text may contain arbitrary Unicode. Trimming uses Unicode whitespace;
lowercasing is Unicode-aware and not locale-specific. It can change string
length. It does not perform Unicode normalization or full case folding:
"Straße" becomes "straße", which remains different from "strasse".
Internal whitespace and other characters remain unless lowercasing changes
them. Unicode tables can change with Rust versions.
describe returns "allow" for Allow, "ban:" followed by the unchanged
string for Ban, and "table:512:" followed by the first byte in decimal for
Table. Keep the table length at 512. rule_report returns exactly
["allow", "ban:spam", "table:512:7"].
The harness checks that report, the normalized entries and the enum size. Passing also requires compilation and Clippy with warnings treated as errors.
Box the large payload
Change Table([u8; 512]) to Table(Box<[u8; 512]>), and wrap its array
constructor in Box::new. The existing describe implementation can still
call len() and index the boxed array through dereferencing.
An inline enum must have enough storage for any of its variants. Boxing the
array moves that payload into a separate allocation, reducing the space each
enum occupies. It adds allocation and indirect access for Table values.
Whether that tradeoff helps a real program depends on its workload.
The whole enum does not become pointer-sized: Ban still contains a String.
Alignment and representation choices also matter, and a source-level move
does not guarantee a particular machine-level copy. Use size_of to inspect
a build; do not infer an exact enum size from its largest field plus a tag.
See the Rust layout reference.
Match each parameter to the operation
The helper signatures illustrate three different needs:
| Helper | Why this parameter fits |
|---|---|
is_banned(&str, &[String]) |
Reads text and a sequence without needing their owning containers. |
push_unique(&mut Vec<String>, &str) |
May append a string, so it needs mutable access to the Vec. |
describe(&Rule) |
Reads a rule while its caller retains ownership. |
The const _: assignments ensure those function types still match. They do
not prove that a function is pure or enforce its algorithm.
normalise takes ownership of both vectors because that is this exercise's
public API. Its implementation consumes them, but its algorithm could also
be exposed through a borrowing API. Returning owned strings does not by
itself require owned inputs or guarantee reuse of their allocations.
Prefer &str or &[T] when a function only needs the borrowed view. Clippy's
ptr_arg helps identify unnecessary container references, but container
methods or other API requirements can justify a narrower type. A mutable
slice cannot replace the Vec in push_unique, because a slice cannot grow.
Accepting a value that converts into String
impl Into<String> is useful when a function will retain an owned string.
This complete example accepts both borrowed text and an existing String:
fn add(names: &mut Vec<String>, name: impl Into<String>) {
names.push(name.into());
}
fn main() {
let mut names = Vec::new();
add(&mut names, "Ada");
let owned = String::from("Lin");
add(&mut names, owned);
assert_eq!(names, ["Ada", "Lin"]);
}
Converting &str creates owned text; converting a String moves that value.
The conversion runs inside add. A generic parameter can be instantiated
for different argument types, but final code size depends on optimization.
For a function that only reads text, &str or an appropriate AsRef<str>
bound expresses a different contract: access to a borrowed view rather than
conversion into an owned String.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.