Skip to content

Q033. How do enums and pattern matching affect ownership?

Short Answer

An enum owns the fields of its active variant. Pattern matching inspects the discriminant and then moves, copies, or borrows bound fields according to their types and binding modes. Binding a non-Copy field by value moves it; matching through a reference borrows it. Only fields of the active variant are destroyed when the enum is dropped.

Deep Dive

Patterns are ownership operations, not only control flow. A binding such as Message(text) binds text by value and moves a String from an owned enum. Message(ref text) borrows the field, while matching &event uses match ergonomics to produce borrowed bindings automatically.

Copyable fields can be bound by value without invalidating their source. A pattern can also move some fields and borrow or copy others, creating a partial move when the aggregate permits it.

The discriminant tells Rust which variant is active. Drop glue destroys only that variant's initialized fields, so inactive variants impose no runtime cleanup.

Internal Model

rustc lowers a match into discriminant tests and control-flow branches. Each arm applies move or borrow operations to the selected field projections, and move analysis merges their initialization states after the match.

Borrowing the scrutinee preserves ownership. Matching by value does not inherently deep-copy payloads; non-Copy bindings transfer them into arm-local places.

Example

rust
enum Event {
    Message(String),
    Tick,
}

fn main() {
    let event = if std::env::args().len() > 1 {
        Event::Message(String::from("hello"))
    } else {
        Event::Tick
    };

    match &event {
        Event::Message(text) => println!("borrowed: {text}"),
        Event::Tick => println!("borrowed tick"),
    }

    match event {
        Event::Message(text) => println!("owned: {text}"),
        Event::Tick => println!("owned tick"),
    }
}

Common Mistakes

  • Assuming every match value expression necessarily moves every field.
  • Forgetting that binding a non-Copy payload by value transfers ownership.
  • Expecting inactive enum variants to have initialized fields that must be dropped.

Follow-ups

  • How do ref and ref mut differ from matching &T and &mut T?
  • When does a pattern create a partial move?
  • How does match ergonomics choose a default binding mode?

References

  • The Rust Programming Language
  • Rust Reference