Data and state modeling: structs, interfaces, slices, and maps without enum-heavy design
Model data clearly in Go without expecting Rust's enum-rich approach to transfer directly.
by the end of this lesson you can
- →Uses a straightforward Go representation
- →Keeps branching easy to scan
- →Avoids pretending interfaces or structs are enum clones
Overview
Rust developers often reach for enums and exhaustive matches to make states explicit. Go gives you fewer modeling tools, so the practical skill is learning when a struct, interface, slice, or map is enough and when extra type precision is not buying much.
In Rust, you often
model variants and state transitions directly with enums and rely on exhaustive matching to keep code honest.
In Go, the common pattern is
to use plain structs, small interfaces, slices, and maps, accepting that some state discipline lives in code paths rather than in the type system.
why this difference matters
This lesson is about letting Go stay simple without pretending it has Rust's modeling vocabulary.
Rust
enum Job {
Queued,
Running,
Failed(String),
Done,
}Go
type Job struct {
Status string
Error string
}Deeper comparison
Rust version
match job {
Job::Queued => run(job_id)?,
Job::Failed(message) => println!("{}", message),
Job::Running => {}
Job::Done => {}
}Go version
switch job.Status {
case "queued":
return run(job.ID)
case "failed":
log.Println(job.Error)
}
return nilReflect
When is a simpler Go model good enough even if it cannot encode every legal state as precisely as Rust?
what a strong answer notices
A strong answer mentions readability, team conventions, and choosing clear control flow over type-system ambition.
Rewrite
Rewrite this Rust enum-based state example into a Go shape that stays readable.
Rewrite this Rust
enum Response {
Ok(String),
NotFound,
Invalid(String),
}what good looks like
- Uses a straightforward Go representation
- Keeps branching easy to scan
- Avoids pretending interfaces or structs are enum clones
Practice
Design a Go data pipeline over slices where each step is clear without introducing extra modeling layers.
success criteria
- Uses slices and structs intentionally
- Keeps interfaces narrow or absent unless behavior abstraction is needed
- Accepts simpler state representation where appropriate
Common mistakes
- Over-modeling Go code because Rust made the state space explicit.
- Using interfaces when plain structs and functions would be clearer.
- Treating simple Go representations as inherently sloppy rather than context-appropriate.
takeaways
- ●This lesson is about letting Go stay simple without pretending it has Rust's modeling vocabulary.
- ●A strong answer mentions readability, team conventions, and choosing clear control flow over type-system ambition.
- ●Uses slices and structs intentionally