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 }