Instead of exposing the fields in a struct like that, you may consider using constructor function to have better control over what the user can set into the struct. That way you don't need to worry about the user setting invalid values. For instance,
```Go
type EmailEvent struct{
id string
date time.Time
description string
}
func NewEmailEvent(id string, date time.Time, description string) EmailEvent {
return EmailEvent{id,date,description}
}
func NewEmailEventByDescription(description string) EmailEvent {
return EmailEvent{
id: randomID(), //validate or set the default value for optional or missing fields
date: time.Now(),
description: description,
}
}
```
Or you can getter and setter methods instead of constructors.
```Go
func (e *EmailEvent) SetID(id string) {
//validate id
if id == "" {
//do something
}
}
```
Or you can set the missing value upon use.
```
func (e EmailEvent) Write(writer io.Writer) {
}
//do processing here
}
```
About type variant, the common way is to use interface as others have suggested since it is easily extensible. Alternatively, you may merge them into one struct. This is less flexible but may be useful when you are dealing with small type variants. For example:
```Go
type EventType int
const (
Undefined EventType = iota
DisplayEvent
MailEvent
)
type Event struct {
from string
to string
description string
eventType EventType
}
func NewDisplayEvent(desc string) Event {
return Event {
description:desc,
eventType: DisplayEvent,
}
}
func NewMailEvent(from, to, desc string) Event {
return Event {
from: from,
to: to,
description: desc,
eventType: MailEvent,
}
}
//implement your getters here
```
I hope this helps.