Fundamentals 17 min read

Using Finite State Machines in Go with the looplab/fsm Package

This article introduces the concept of finite state machines, demonstrates how to implement them in Go using the looplab/fsm library with practical code examples, explains the lifecycle callbacks, and shows how to apply FSMs to real‑world scenarios such as door state management.

Go Programming World
Go Programming World
Go Programming World
Using Finite State Machines in Go with the looplab/fsm Package

In programming, a finite state machine (FSM) is an elegant tool for managing complex state transitions, defined by states, events, and transition rules that modularize business logic. This article explores how to use FSMs in Go.

Finite State Machine

Before introducing FSMs, consider a simple example program:

https://github.com/jianghushinian/blog-go-example/blob/main/fsm/main.go
package main

import (
    "fmt"
)

type State string

const (
    ClosedState State = "closed"
    OpenState   State = "open"
)

type Event string

const (
    OpenEvent  Event = "open"
    CloseEvent Event = "close"
)

type Door struct {
    to    string
    state State
}

func NewDoor(to string) *Door {
    return &Door{to: to, state: ClosedState}
}

func (d *Door) CurrentState() State { return d.state }

func (d *Door) HandleEvent(e Event) {
    switch e {
    case OpenEvent:
        d.state = OpenState
    case CloseEvent:
        d.state = ClosedState
    }
}

func main() {
    door := NewDoor("heaven")
    fmt.Println(door.CurrentState())
    door.HandleEvent(OpenEvent)
    fmt.Println(door.CurrentState())
    door.HandleEvent(CloseEvent)
    fmt.Println(door.CurrentState())
}

The example defines a core struct Door with fields to (the destination of the door) and state (the current state, either open or closed ). The main function creates a door, prints its initial state, triggers an open event, prints the new state, then triggers a close event and prints the final state.

$ go run main.go
closed
open
closed

This demonstrates that a door is a concrete model of an FSM.

An FSM (finite‑state machine) is a mathematical computation model with three core characteristics:

• The number of states is finite.

• At any moment the system is in exactly one state.

• Under certain conditions (triggered by an event) the system transitions from one state to another.

Any object satisfying these three properties can be regarded as an FSM. For the Door object, the states are open and closed ; an open event moves the door from closed to open , and a close event does the opposite.

FSMs appear everywhere in daily life—traffic lights, order processing in e‑commerce, etc.

Using the looplab/fsm Package

Installation

Install the package with:

$ go get github.com/looplab/fsm

Simple Usage

Rewrite the earlier Door example using the fsm package:

https://github.com/jianghushinian/blog-go-example/blob/main/fsm/examples/simple.go
package main

import (
    "context"
    "fmt"
    "github.com/looplab/fsm"
)

func main() {
    fsm := fsm.NewFSM(
        "closed",
        fsm.Events{
            {Name: "open", Src: []string{"closed"}, Dst: "open"},
            {Name: "close", Src: []string{"open"}, Dst: "closed"},
        },
        fsm.Callbacks{},
    )
    fmt.Println(fsm.Current())
    _ = fsm.Event(context.Background(), "open")
    fmt.Println(fsm.Current())
    _ = fsm.Event(context.Background(), "close")
    fmt.Println(fsm.Current())
}

The fsm.NewFSM function creates an FSM object. The first argument is the initial state, the second is a slice of events defining source and destination states, and the third is a map of callbacks (empty in this simple example).

$ go run examples/simple.go
closed
open
closed

Using the fsm package abstracts the state‑transition logic, reducing bugs and improving maintainability.

Embedding FSM in a Struct

The package can also be used as a field inside a struct:

https://github.com/jianghushinian/blog-go-example/blob/main/fsm/examples/struct/struct.go
package main

import (
    "context"
    "fmt"
    "github.com/looplab/fsm"
)

type Door struct {
    To  string
    FSM *fsm.FSM
}

func NewDoor(to string) *Door {
    d := &Door{To: to}
    d.FSM = fsm.NewFSM(
        "closed",
        fsm.Events{{Name: "open", Src: []string{"closed"}, Dst: "open"}, {Name: "close", Src: []string{"open"}, Dst: "closed"}},
        fsm.Callbacks{"enter_state": func(_ context.Context, e *fsm.Event) {d.enterState(e)}},
    )
    return d
}

func (d *Door) enterState(e *fsm.Event) {
    fmt.Printf("The door to %s is %s\n", d.To, e.Dst)
}

func main() {
    door := NewDoor("heaven")
    _ = door.FSM.Event(context.Background(), "open")
    _ = door.FSM.Event(context.Background(), "close")
}

Here the FSM is a field of Door , and a callback prints a message whenever the state changes.

$ go run examples/struct/struct.go
The door to heaven is open
The door to heaven is closed

The package provides eight lifecycle callbacks. They are grouped into four categories: before (executed before an event), leave (executed when leaving a state), enter (executed when entering a state), and after (executed after an event). The article lists each callback (e.g., before_open , leave_closed , enter_open , after_open ) and explains their execution order and precedence.

Callbacks can be defined with shorthand keys such as "closed" for enter_closed or "close" for after_close . The article also shows how to handle unknown events or states.

Conclusion

The article introduced FSM concepts, demonstrated how to implement a door state machine in Go using the fsm library, and highlighted the benefits of pre‑defining transition rules and lifecycle callbacks to reduce bugs and improve code clarity. It points readers to further resources, including the official fsm examples, the OneX nightwatch component, and the source repository.

backendprogramminggoState MachineFinite State Machinefsm
Go Programming World
Written by

Go Programming World

Mobile version of tech blog https://jianghushinian.cn/, covering Golang, Docker, Kubernetes and beyond.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.