Question

Access Enum Variant Values in Rust with Pattern Matching

Question

Given the following Rust enum and value:

struct Point {
    x: f64,
    y: f64,
}

enum Shape {
    Circle(Point, f64),
    Rectangle(Point, Point),
}

let my_shape = Shape::Circle(Point { x: 0.0, y: 0.0 }, 10.0);

How can you access and print the second value stored in the Circle variant, which is 10.0? Attempts such as my_shape.last and my_shape.second do not work.

Short Answer

Rust enum variants can hold different kinds of data, so their contents are accessed by pattern matching, not by positional properties such as .second. You will learn to extract a circle's radius with match or if let, choose the right tool, and avoid ownership-related surprises.

Concept

An enum represents a value that can be one of several variants. In this example, a Shape is either:

  • Shape::Circle(Point, f64): a center point and a radius
  • Shape::Rectangle(Point, Point): two points

Because each variant can contain a different number and type of values, Rust cannot provide a universal field like my_shape.second. A Rectangle has two Point values, while a Circle has a Point and an f64.

Instead, use pattern matching to ask Rust which variant you have and bind its stored values to local variables. This is type-safe: Rust only lets you use radius inside code where it has proved that the shape is a Circle.

For tuple-style enum variants such as Circle(Point, f64), values are extracted by writing a matching pattern:

Shape::Circle(center, radius)

Here, center receives the Point and radius receives the f64.

Mental Model

Think of Shape as a labeled package that may contain different contents.

  • A package labeled Circle contains a center coordinate and a radius.
  • A package labeled Rectangle contains two corner coordinates.

Before taking out the second item, you must inspect the package label. Pattern matching is Rust's safe way to open the correct kind of package and name the contents.

my_shape.second would mean every possible Shape has one consistent second field. That is not true, so Rust requires you to handle the variant first.

Take Quiz

Syntax and Examples

Use match when you want to handle every possible variant:

struct Point {
    x: f64,
    y: f64,
}

enum Shape {
    Circle(Point, f64),
    Rectangle(Point, Point),
}

fn main() {
    let my_shape = Shape::Circle(Point { x: 0.0, y: 0.0 }, 10.0);

    match my_shape {
        Shape::Circle(_, radius) => println!("Circle radius: {radius}"),
        Shape::Rectangle(_, _) => println!("This is a rectangle."),
    }
}

The _ pattern means “there is a value here, but do not bind it because it is not needed.” The variable radius receives 10.0.

If you only care about the Circle case, use if let:

if let Shape::Circle(_, radius) = my_shape {
    println!("Circle radius: {radius}");
}

if let runs its body only when my_shape is a Circle. If it is a Rectangle, nothing is printed.

Step by Step Execution

Consider this code:

let my_shape = Shape::Circle(Point { x: 0.0, y: 0.0 }, 10.0);

if let Shape::Circle(_, radius) = my_shape {
    println!("{radius}");
}

Step by step:

  1. my_shape stores the enum value Shape::Circle(...).
  2. The if let pattern checks whether the value has the Shape::Circle variant.
  3. It does match, because the value is a circle.
  4. _ matches the first stored value, the Point, and intentionally ignores it.
  5. radius is bound to the second stored value, 10.0.
  6. println! prints 10 or 10.0 depending on Rust's default display formatting for the value.

If my_shape contained Shape::Rectangle(...), the pattern would not match and the body would be skipped.

Real World Use Cases

Pattern matching enum data is common whenever a program has several valid kinds of state or result:

  • UI drawing: draw a circle using its center and radius, or draw a rectangle using its corners.
  • API results: extract successful data from Result::Ok(value) or handle Result::Err(error).
  • Optional values: access an existing value in Option::Some(value) while handling None.
  • Events: respond differently to events such as Click, KeyPress, and WindowResized.
  • Parsers: process tokens such as numbers, identifiers, operators, and strings.
  • Application state: handle states like Loading, Ready(data), and Failed(message).

Enums make these alternatives explicit, and pattern matching makes handling each alternative safe.

Take Quiz

Real Codebase Usage

In production Rust code, developers usually give variant fields meaningful names when the data has a clear role. Named fields can be easier to read than tuple positions:

struct Point {
    x: f64,
    y: f64,
}

enum Shape {
    Circle { center: Point, radius: f64 },
    Rectangle { top_left: Point, bottom_right: Point },
}

fn print_radius(shape: &Shape) {
    if let Shape::Circle { radius, .. } = shape {
        println!("Radius: {radius}");
    }
}

.. means “ignore the other fields.” Passing &Shape and matching against shape borrows the shape rather than moving it, so the caller can continue using it afterward.

Use these common patterns:

  • Use match when every variant needs an outcome.
  • Use if let for one variant of interest.
  • Use let ... else when a specific variant is required before the function can continue.
  • Use named variant fields when positional fields become unclear.
  • Match on &value when you need to inspect a value without taking ownership of it.

Common Mistakes

Trying to use a field that does not exist

This does not compile:

// let radius = my_shape.second;

Shape is an enum, not a struct with a field named second. It may contain different variants with different data.

Forgetting to handle all variants with match

This is incomplete:

match my_shape {
    Shape::Circle(_, radius) => println!("{radius}"),
}

Rust rejects it because my_shape could be a Rectangle. Add a Rectangle arm or use if let if ignoring rectangles is intentional.

match my_shape {
    Shape::Circle(_, radius) => println!("{radius}"),
    Shape::Rectangle(_, _) => {}
}

Accidentally moving the enum value

Matching by value can move data out of an enum:

let shape = Shape::Circle(Point { x: 0.0, y: 0.0 }, 10.0);

match shape {
    Shape::Circle(_, radius) => println!("{radius}"),
    Shape::Rectangle(_, _) => {}
}

// `shape` may not be usable here because it was moved.

To inspect without consuming it, match on a reference:

if let Shape::Circle(_, radius) = &shape {
    println!("{radius}");
}

Using tuple fields when names would improve readability

Circle(Point, f64) is valid, but readers must remember that the second item is the radius. For larger domain models, prefer Circle { center, radius }.

Comparisons

ApproachBest whenExample
matchEvery enum variant needs handlingmatch shape { Shape::Circle(..) => ..., Shape::Rectangle(..) => ... }
if letYou care about one variant and can ignore the othersif let Shape::Circle(_, radius) = shape { ... }
let ... elseA function can continue only for one expected variantlet Shape::Circle(_, radius) = shape else { return; };
Tuple variantValues are short and their order is obviousCircle(Point, f64)
Struct-like variantValues have meaningful roles and readability mattersCircle { center: Point, radius: f64 }

A regular struct has fields that can be accessed directly:

struct Circle {
    center: Point,
    radius: f64,
}

let circle = Circle {
    center: Point { x: 0.0, y: 0.0 },
    radius: 10.0,
};

println!("{}", circle.radius);

An enum needs matching first because its current variant determines which data exists.

Take Quiz

Cheat Sheet

// Extract tuple-variant values with match
match shape {
    Shape::Circle(center, radius) => println!("{radius}"),
    Shape::Rectangle(a, b) => println!("rectangle"),
}

// Ignore values with `_`
if let Shape::Circle(_, radius) = shape {
    println!("{radius}");
}

// Borrow instead of moving the enum
if let Shape::Circle(_, radius) = &shape {
    println!("{radius}");
}

// Match a named-field variant
if let Shape::Circle { radius, .. } = &shape {
    println!("{radius}");
}

// Require one variant, otherwise leave early
let Shape::Circle(_, radius) = shape else {
    return;
};

Rules:

  • Enum contents are accessed with patterns, not .first, .second, or .last.
  • match must cover all possible variants.
  • _ ignores one value; .. ignores remaining fields.
  • Matching by value can move the value; use &shape to borrow it.

FAQ

Can I access a Rust enum tuple field with .0 or .1?

No. .0 and .1 work on tuples and tuple structs, but not directly on an enum because the enum may currently be a different variant. Match the variant first.

How do I print the radius only if the shape is a circle?

Use if let:

if let Shape::Circle(_, radius) = &shape {
    println!("{radius}");
}

Why does match require a rectangle branch too?

A Shape can be a Circle or a Rectangle. Rust requires exhaustive matching so no possible case is silently forgotten.

Does pattern matching copy the radius?

It depends on how you match. Matching shape by value moves its contents. Matching &shape borrows them. Since f64 implements Copy, the radius is easy to copy when needed, but the surrounding enum may still be moved by a by-value match.

Should I use tuple variants or named fields?

Use tuple variants for small, obvious groups of values. Use named fields when field roles matter, such as Circle { center, radius }.

Can I extract the radius in a function?

Yes. For example:

fn radius_of(shape: &Shape) -> Option<f64> {
    match shape {
        Shape::Circle(_, radius) => Some(*radius),
        Shape::Rectangle(_, _) => None,
    }
}
Take Quiz

Mini Project

Description

Build a small shape-reporting program. It stores circles and rectangles in a vector, then uses pattern matching to print useful information for each shape. This demonstrates why enum data must be extracted according to its active variant.

Goal

Print a description for every shape and calculate the area of each circle.

Requirements

Create Point and Shape types. Store at least two circles and one rectangle in a vector. Use match to handle both Shape variants. Print each circle's center, radius, and area. Print each rectangle's two points.

Take Quiz

Keep learning

Related questions

View more