Skip to content

← Shape Your Data: Structs, Enums, Pattern Matching step 6 of 26

Easy Primitives

Represent commands with enum variants

Complete the Event enum and its renderer. Add the missing struct variant Move { x: i64, y: i64 }, then add the missing ChangeColor match arm. Keep the supplied parser and describe_events function.

pub fn describe_events(lines: Vec<String>) -> Vec<String>

Return one description per input line, preserving order and duplicates. An empty input vector produces an empty output vector.

Command rules

The parser splits each line with split_whitespace(). Leading and trailing Unicode whitespace is ignored, and consecutive whitespace characters separate words without producing empty words. Command names are case-sensitive.

Words after splitting Output
quit quit
move x y move to (x, y) with parsed decimal coordinates
write followed by zero or more words write followed by the quoted, escaped text
color r g b color #rrggbb in lowercase hexadecimal
Any other command, wrong argument count or invalid number unknown

quit, move and color require exactly the word counts shown. Coordinates must parse as i64; color components must parse as u8 (0 through 255). The standard integer parsers accept a leading + and leading zeros; coordinates can also be negative. Decimal points, underscores, out-of-range values and negative color components are rejected.

For write, join the remaining words with one ASCII space. This normalizes whitespace rather than preserving the original spacing. The renderer uses Rust's Debug string formatting ({text:?}), which adds double quotes and escapes characters such as quotes and backslashes. Quotes in the input are ordinary text, not a command-language quoting syntax.

Input line Description
move +03 -4 move to (3, -4)
write hello world write "hello world"
write write ""
color 255 0 128 color #ff0080
color 300 0 0 unknown
quit now or an empty line unknown

Each color component needs two hexadecimal digits, including a leading zero when necessary. Preserve the supplied Debug formatting for text; it is not JSON string serialization and its details can vary with the Rust toolchain.

Variants carry different data

An enum value selects one variant and carries that variant's fields. This is called a sum type: the alternatives belong to one type, even though their payloads differ.

This exercise uses three shapes:

  • Quit is a unit variant with no fields.
  • Write(String) and ChangeColor(u8, u8, u8) are tuple variants with positional fields.
  • Move { x: i64, y: i64 } is a struct variant with named fields.

The type prevents combinations such as a Quit event carrying a message. It does not validate every application rule: a coordinate may still be outside a game's map, for example. That would need a separate check.

Tuple variant constructors can be passed to functions such as map. Here is a complete example using a smaller enum:

#[derive(Debug, PartialEq)]
enum Message {
    Quit,
    Text(String),
}

fn text_message(text: Option<String>) -> Option<Message> {
    text.map(Message::Text)
}

fn main() {
    let message = text_message(Some(String::from("hello")));
    assert_eq!(message, Some(Message::Text(String::from("hello"))));

    use Message::Quit;
    assert_eq!(Quit, Message::Quit);
}

Message::Text is a callable constructor. It can also coerce to a function pointer of type fn(String) -> Message. Use a qualified name such as Message::Quit, or bring the variant into scope with use as above.

Match each case

The parser and renderer already refer to Event::Move, so the missing variant causes compilation to fail. Once you add it, the renderer still lacks a case for ChangeColor. An exhaustive match must cover every possible input; the compiler reports E0004 for that missing case.

The supplied slice patterns describe the accepted word counts. For example, ["move", x, y] accepts exactly three words, while ["write", rest @ ..] accepts all words after write. Parsing can return None; the supplied driver turns that into "unknown" without dropping the input line.

The starter's allow(dead_code) attribute suppresses unused-code warnings. Such warnings depend on how code is referenced, not which inputs happen to appear in the tests. Your repair must compile, match the expected descriptions and pass Clippy with warnings treated as errors.