DEV Community

Cover image for Go Basics: How to Use Enums, Structs, and Interfaces Effectively
Athreya aka Maneshwar
Athreya aka Maneshwar

Posted on

Go Basics: How to Use Enums, Structs, and Interfaces Effectively

Hi there! I'm Maneshwar. Currently, I’m building a private AI code review tool that runs on your LLM key (OpenAI, Gemini, etc.) with flat, no-seat pricing — designed for small teams. Check it out, if that’s your kind of thing.


Go doesn’t have traditional enums like some other languages, but that doesn’t stop us from implementing enum-like behavior.

Combine that with powerful struct and interface capabilities, and you get clean, maintainable, type-safe code.

Let’s break it down with minimal theory and direct usage.

Enum in Go (aka Typed Constants)

Go doesn’t have a native enum keyword, but you can get enum-like behavior using const + iota.

type Status int

const (
    Pending Status = iota
    Processing
    Completed
    Failed
)

func (s Status) String() string {
    return [...]string{"Pending", "Processing", "Completed", "Failed"}[s]
}
Enter fullscreen mode Exit fullscreen mode

Why use this?

  • Type safety.
  • Can attach methods (like .String()).
  • Cleaner switch-case usage.
func HandleStatus(s Status) {
    switch s {
    case Pending:
        fmt.Println("Pending...")
    case Completed:
        fmt.Println("Done.")
    default:
        fmt.Println("Unknown status.")
    }
}
Enter fullscreen mode Exit fullscreen mode

Struct in Go

struct is your bread and butter in Go — a collection of fields. Think of it like a lightweight class without inheritance.

type User struct {
    ID    int
    Name  string
    Email string
}
Enter fullscreen mode Exit fullscreen mode

You can attach methods:

func (u User) IsEmailVerified() bool {
    return strings.HasSuffix(u.Email, "@verified.com")
}
Enter fullscreen mode Exit fullscreen mode

Structs are passed by value unless you use a pointer (*User).

Interface in Go

Interfaces in Go are satisfied implicitly. If a type implements the method(s), it satisfies the interface — no need to declare it.

type Notifier interface {
    Notify(message string)
}
Enter fullscreen mode Exit fullscreen mode

Implement it:

type EmailNotifier struct {
    Email string
}

func (e EmailNotifier) Notify(msg string) {
    fmt.Printf("Sending email to %s: %s\n", e.Email, msg)
}
Enter fullscreen mode Exit fullscreen mode

Use it:

func SendAlert(n Notifier) {
    n.Notify("Server down")
}
Enter fullscreen mode Exit fullscreen mode

Putting It Together

Here’s how all 3 concepts come together.

type AlertLevel int

const (
    Info AlertLevel = iota
    Warning
    Critical
)

type Alert struct {
    Level   AlertLevel
    Message string
}

type Notifier interface {
    Notify(message string)
}

type SlackNotifier struct {
    Channel string
}

func (s SlackNotifier) Notify(msg string) {
    fmt.Printf("Sending Slack message to %s: %s\n", s.Channel, msg)
}

func SendAlert(n Notifier, a Alert) {
    n.Notify(fmt.Sprintf("[%s] %s", a.Level.String(), a.Message))
}

func (a AlertLevel) String() string {
    return [...]string{"INFO", "WARNING", "CRITICAL"}[a]
}
Enter fullscreen mode Exit fullscreen mode

Usage

func main() {
    slack := SlackNotifier{Channel: "#ops"}
    alert := Alert{
        Level:   Critical,
        Message: "Database connection lost",
    }
    SendAlert(slack, alert)
}
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

  • Use const + iota for enums.
  • Use struct to group data and behavior.
  • Use interface for decoupled, flexible code.

That’s it. No magic — just Go’s simple but powerful type system.


LiveReview helps you get great feedback on your PR/MR in a few minutes.

Saves hours on every PR by giving fast, automated first-pass reviews. Helps both junior/senior engineers to go faster.

If you're tired of waiting for your peer to review your code or are not confident that they'll provide valid feedback, here's LiveReview for you.

Top comments (0)