0

Given the following types:

type Event interface{}

type ActionResultEvent struct {
    Result string
}

type ActionSuccessEvent ActionResultEvent
type ActionFailureEvent ActionResultEvent

type eventHandleFunc func(e Event)

My goal is to have event handlers (of type eventHandleFunc) for the concrete types ActionSuccessEvent, ActionFailureEvent, as well as for the more abstract ActionResultEvent. The latter I would like to use for both ActionSuccessEvents and ActionFailureEvents.

Now my idea was to just type cast the Event interface to the structure I would like to work on and that works nicely for the concrete types.

// Working perfectly fine
func(e Event) {
  event := e.(ActionFailureEvent)
  fmt.Println(event.Result)
} (ActionFailureEvent{ Result: "failure" })

func(e Event) {
  event := e.(ActionSuccessEvent)
  fmt.Println(event.Result)
} (ActionSuccessEvent{ Result: "success" }) 

Now what about handlers accepting ActionResultEvents? My absolute favourite would look like this one:

func(e Event) {
  event := e.(ActionResultEvent)
  fmt.Println(event.Result)
} (ActionSuccessEvent{ Result: "success" })    

This obviously panics as e was of type ActionSuccessEvent.

Then we can of course cast to the initial type and back to intermediate:

// Works but would that would need to change whenever new types "extending" 
// ActionResultEvent are added
func(e Event) {
  var resultEvent ActionResultEvent

  switch e.(type) {
  case ActionSuccessEvent:
    resultEvent = ActionResultEvent(e.(ActionSuccessEvent))

  case ActionFailureEvent:
    resultEvent = ActionResultEvent(e.(ActionFailureEvent))
  }
  fmt.Println(resultEvent.Result)
} (ActionSuccessEvent{ Result: "success" })

Another really nice approach from my point would be:

// Error: use of e.(type) outside type switch
func(e Event) {
  resultEvent := ActionResultEvent(e.(type))
} (ActionSuccessEvent{ Result: "success" })

Can anybody think of a smooth solution? One side note: I am happy if the handler panics during runtime whenever a typecast fails, as the wrapper will recover from that.

This is the example code above on the playground. Thanks!

tworabbits
  • 1,203
  • 12
  • 17
  • 1
    "a smooth solution": Remodel. You cannot have smooth traditional OOP in Go. Try modeling it as interface types and compose them. – Volker Mar 26 '18 at 03:10
  • Ok, don't really get the down vote but hey. Would you have any examples or resources that might point me in the right direction? – tworabbits Mar 26 '18 at 03:13
  • I probably have to rephrase this: I am not looking for a solution to make the code above work. I am actually trying to understand how to address this general problem in a "go way" – tworabbits Mar 26 '18 at 03:17
  • Go doesn't have generics. [Do this instead.](https://appliedgo.net/generics/) – Michael Hampton Mar 26 '18 at 03:57
  • @MichaelHampton Thanks for your comment, very interesting article. I found a solution using interfaces and interface composition. – tworabbits Mar 26 '18 at 04:52

1 Answers1

1

Although I got down votes here, I believe this could be of interest to others. So I decided to post the solution to what I have figured out to work: See the playground

If this is bad practice or not "the go way" of doing things I would be really happy to get some feedback. Thanks!

tworabbits
  • 1,203
  • 12
  • 17