github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/safe-state/state.go (about)

     1  // safe-state shows an example how to do struct changes and persist them only on a non-nil error.
     2  package main
     3  
     4  import (
     5  	"fmt"
     6  )
     7  
     8  type Ship struct {
     9  	Vel Vector
    10  }
    11  
    12  type Vector struct {
    13  	X, Y float64
    14  }
    15  
    16  type Event interface{}
    17  
    18  type Input struct {
    19  	Dir Vector
    20  }
    21  
    22  func (ship *Ship) guard() (*Ship, func(err *error)) {
    23  	x := *ship
    24  	return &x, func(err *error) {
    25  		if *err == nil {
    26  			*ship = x
    27  		}
    28  	}
    29  }
    30  
    31  func (ship *Ship) Handle(ev Event) (err error) {
    32  	defer ship.guardo(&ship)(&err)
    33  
    34  	switch ev := ev.(type) {
    35  	case Input:
    36  		ship.Vel.X += ev.Dir.X
    37  		ship.Vel.Y += ev.Dir.Y
    38  		if ship.Vel.X > 10 || ship.Vel.Y > 10 {
    39  			return fmt.Errorf("velocity too large")
    40  		}
    41  	default:
    42  		return fmt.Errorf("invalid event %T", ev)
    43  	}
    44  
    45  	return nil
    46  }
    47  
    48  func (ship *Ship) guardo(p **Ship) func(err *error) {
    49  	x := *ship
    50  	*p = &x
    51  	return func(err *error) {
    52  		if *err == nil {
    53  			*ship = x
    54  		}
    55  	}
    56  }
    57  
    58  func (ship *Ship) Handleo(ev Event) (err error) {
    59  	defer ship.guardo(&ship)(&err)
    60  
    61  	switch ev := ev.(type) {
    62  	case Input:
    63  		ship.Vel.X += ev.Dir.X
    64  		ship.Vel.Y += ev.Dir.Y
    65  		if ship.Vel.X > 10 || ship.Vel.Y > 10 {
    66  			return fmt.Errorf("velocity too large")
    67  		}
    68  	default:
    69  		return fmt.Errorf("invalid event %T", ev)
    70  	}
    71  
    72  	return nil
    73  }
    74  
    75  func main() {
    76  	{
    77  		ship := Ship{}
    78  		for i := 0; i < 6; i++ {
    79  			err := ship.Handle(Input{Dir: Vector{1, 2}})
    80  			fmt.Println(ship, err)
    81  		}
    82  	}
    83  	{
    84  		ship := Ship{}
    85  		for i := 0; i < 6; i++ {
    86  			err := ship.Handleo(Input{Dir: Vector{1, 2}})
    87  			fmt.Println(ship, err)
    88  		}
    89  	}
    90  }