title: Go maps and slices with mixed struct types
body: <p>I'm trying to learn how to work with Go by creating a simple event store with the ability to create projections. I get stuck on how to work with slices and maps that contains structs of mixed types. The point with this would be that I want a developer to create as many structs implementing IEntity and IEvent as needed with various fields.</p>
  
  <p>I come from a JavaScript/Node.js background with some basic knowledge of C/C++/Java and I'm probably looking to much for class/inheritance patterns here and need some help on how to get the same functionality in Go.</p>
  
  <pre><code>package main
  
  import (
      "sync"
      "time"
  
      uuid "github.com/satori/go.uuid"
  )
  
  // IEntity describes an entity, a struct that is the sum of all events resolved in a chronological order
  type IEntity interface {
  }
  
  // IProjection describes the interface for a projection struct
  type IProjection interface {
      Lock()
      Unlock()
      Get(id uuid.UUID) IEntity
      Set(id uuid.UUID, entity IEntity)
      GetLastEventTime() time.Time
      Append(event IEvent)
  }
  
  // IEvent describes the interface for a event, any event added to the system needs to implement this interface
  type IEvent interface {
      Resolve(projection IProjection)
  }
  
  // EventVault is the base struct that keeps all the events and allows for projections to be created
  type EventVault struct {
      sync.Mutex
      events []IEvent
  }
  
  // Append adds a IEvent to the projection and runs that events IEvent.Resolve method
  func (vault *EventVault) Append(event IEvent) {
      vault.Lock()
      defer vault.Unlock()
      vault.events = append(vault.events, event)
  }
  
  // Project creates a projection with entities from all events up until the choosen end time
  func (vault *EventVault) Project(endTime time.Time, projection IProjection) IProjection {
      lastEventTime := projection.GetLastEventTime()
      for index := range vault.events {
          event := vault.events[index]
          if event.Time.After(lastEventTime) &amp;&amp; event.Time.Before(endTime) {
              projection.Append(event)
          }
      }
      return projection
  }
  
  // Projection caculates and stores a projection of the events appended to it with the Append method
  type Projection struct {
      sync.Mutex
      events   []IEvent
      entities map[uuid.UUID]IEntity
  }
  
  // Get returns an IEntity struct from an id (for use in the IEvent.Resolve method)
  func (projection *Projection) Get(id uuid.UUID) IEntity {
      return projection.entities[id]
  }
  
  // Set add a IEntity to the projection (for use in the IEvent.Resolve method)
  func (projection *Projection) Set(id uuid.UUID, entity IEntity) {
      projection.entities[id] = entity
  }
  
  // GetLastEventTime returns the time for the event that was added last
  func (projection *Projection) GetLastEventTime() time.Time {
      event := projection.events[len(projection.events)-1]
  
      if event == nil {
          return time.Unix(0, 0)
      }
  
      return event.Time
  }
  
  // Append adds a IEvent to the projection and runs that events IEvent.Resolve method
  func (projection *Projection) Append(event IEvent) {
      projection.Lock()
      projection.events = append(projection.events, event)
      event.Resolve(projection)
      projection.Unlock()
  }
  
  // ------------------ Below is usage of above system ------------------
  
  // PlayerEntity is a sample entity that can be used for testing
  type PlayerEntity struct {
      ID        uuid.UUID
      Name      string
      Birthday  time.Time
      UpdatedAt time.Time
  }
  
  // AddPlayerEvent is a sample event that can be used for testing
  type AddPlayerEvent struct {
      ID   uuid.UUID
      Time time.Time
      Name string
  }
  
  // Resolve will create a new PlayerEntity and add that to the projection
  func (event *AddPlayerEvent) Resolve(projection IProjection) {
      player := PlayerEntity{ID: uuid.NewV4(), Name: event.Name, UpdatedAt: event.Time}
      projection.Set(player.ID, &amp;player)
  }
  
  // SetBirthdayPlayerEvent is a sample event that can be used for testing
  type SetBirthdayPlayerEvent struct {
      ID       uuid.UUID
      Time     time.Time
      Birthday time.Time
  }
  
  // Resolve will change the name on a PlayerEntity
  func (event *SetBirthdayPlayerEvent) Resolve(projection IProjection) {
      player := *projection.Get(event.ID)
      player.Birthday = event.Birthday
      player.UpdatedAt = event.Time
  }
  
  func main() {
      vault := EventVault{}
      event1 := AddPlayerEvent{ID: uuid.NewV4(), Time: time.Now(), Name: "Lisa"}
      vault.Append(&amp;event1)
      birthday, _ := time.Parse("2006-01-02", "2017-03-04")
      event2 := SetBirthdayPlayerEvent{ID: event1.ID, Time: time.Now(), Birthday: birthday}
      vault.Append(&amp;event2)
  }
  </code></pre>
  
  <p>The errors I get are</p>
  
  <pre><code>./main.go:47: event.Time undefined (type IEvent has no field or method Time)
  ./main.go:79: event.Time undefined (type IEvent has no field or method Time)
  ./main.go:122: invalid indirect of projection.Get(event.ID) (type IEntity)
  </code></pre>
  
  <p>The struct types are probably lost so I need another way of storing them to put them into the maps and slices but how?</p>