Data and state modeling: enums and match instead of struct-plus-switch habits
Use enums and pattern matching to model explicit state machines more directly than typical Go code.
by the end of this lesson you can
- →Uses an enum instead of a string tag
- →Carries data on the relevant variant only
- →Uses match instead of ad hoc branching
Overview
Go developers often represent variants with structs, interfaces, constants, or booleans plus switch. Rust gives you enums that carry data and match expressions that force you to handle each state deliberately.
In Go, you often
model states with a struct plus flags, sentinel values, or interface-based branching that relies on convention.
In Rust, the common pattern is
to model variants directly as enum cases and use match to make branching exhaustive and visible.
why this difference matters
This is one of the clearest places where Rust's type system helps you express legal states instead of merely documenting them.
Go
type Job struct {
Status string
Error string
}Rust
enum Job {
Queued,
Running,
Failed(String),
Done,
}Deeper comparison
Go version
switch job.Status {
case "queued":
run(job)
case "failed":
log.Println(job.Error)
}Rust version
match job {
Job::Queued => run(job_id)?,
Job::Failed(message) => println!("{}", message),
Job::Running => {}
Job::Done => {}
}Reflect
What gets safer when valid states are encoded directly rather than implied by string values or boolean combinations?
what a strong answer notices
A strong answer mentions fewer impossible states, more complete branching, and clearer state transitions.
Rewrite
Rewrite this Go tagged-state example into a Rust enum plus match.
Rewrite this Go
type Response struct {
Kind string
Body string
}
if res.Kind == "error" {
log.Println(res.Body)
}what good looks like
- Uses an enum instead of a string tag
- Carries data on the relevant variant only
- Uses match instead of ad hoc branching
Practice
Design a Rust enum for an HTTP request lifecycle with pending, validated, rejected, and handled states.
success criteria
- Represents each state explicitly
- Carries data only where it belongs
- Shows how match would keep handling exhaustive
Common mistakes
- Recreating Go string-status patterns in Rust.
- Using enums only as named constants instead of data-carrying variants.
- Treating match as verbose control flow instead of part of the model.
takeaways
- ●This is one of the clearest places where Rust's type system helps you express legal states instead of merely documenting them.
- ●A strong answer mentions fewer impossible states, more complete branching, and clearer state transitions.
- ●Represents each state explicitly