Structs and Methods
Go has no classes, but combines structs with methods (functions attached to a type) to get most of the same benefits, more explicitly.
type Student struct {
Name string
XP int
}
func (s *Student) GainXP(amount int) {
s.XP += amount
}
func (s Student) Greeting() string {
return "Hi, I'm " + s.Name
}
ada := Student{Name: "Ada", XP: 0}
ada.GainXP(50)
fmt.Println(ada.XP) // 50
fmt.Println(ada.Greeting()) // "Hi, I'm Ada"
func (s *Student) GainXP(...) is a method with s as its receiver, this is what attaches the function to the Student type, letting you call it as ada.GainXP(...) instead of GainXP(ada, ...).
Pointer vs. value receivers
This is the one detail that trips everyone up at first: GainXP uses a pointer receiver (*Student) because it needs to modify the struct, Greeting uses a value receiver (Student) because it only reads from it. A value receiver operates on a copy, changes inside it never affect the original:
func (s Student) BrokenGainXP(amount int) {
s.XP += amount // only modifies the local copy, caller sees no change
}
Rule of thumb: if a method needs to modify the struct, use a pointer receiver, if it only reads, either works, but staying consistent within a type is idiomatic Go.