Go Structs
Defining Structs
type Host struct {
Name string
IP string
Port int
IsActive bool
}
// Constructor function — Go convention, not a language feature
func NewHost(name, ip string, port int) *Host {
return &Host{
Name: name,
IP: ip,
Port: port,
IsActive: true,
}
}
Go has no constructors. The convention is a NewTypeName function that returns a pointer. Fields are exported (visible outside the package) when capitalized, unexported when lowercase.
Methods
// Value receiver — operates on a copy
func (h Host) Address() string {
return fmt.Sprintf("%s:%d", h.IP, h.Port)
}
// Pointer receiver — can modify the struct
func (h *Host) Deactivate() {
h.IsActive = false
}
Use a pointer receiver when the method modifies the struct or when the struct is large (avoids copying). If any method uses a pointer receiver, all methods on that type should use pointer receivers for consistency.
Embedding
type Credentials struct {
Username string
Password string
}
type Device struct {
Host // embedded — fields promoted
Credentials // embedded
Model string
}
d := Device{
Host: Host{Name: "sw01", IP: "10.50.1.10", Port: 22},
Credentials: Credentials{Username: "admin"},
Model: "C9300",
}
// Promoted fields — access directly
fmt.Println(d.Name) // "sw01" — from Host
fmt.Println(d.Username) // "admin" — from Credentials
fmt.Println(d.Address()) // "10.50.1.10:22" — promoted method
Embedding promotes fields and methods from the inner struct. This is composition, not inheritance — there is no type hierarchy, no polymorphism through embedding.
Struct Tags
type Config struct {
Host string `json:"host" validate:"required"`
Port int `json:"port,omitempty"`
LogLevel string `json:"log_level"`
}
c := Config{Host: "localhost", Port: 8080}
data, _ := json.Marshal(c)
// {"host":"localhost","port":8080}
Tags are metadata strings attached to struct fields. json:"name,omitempty" controls JSON key naming and omits zero-value fields. Tags are read at runtime via reflection.
Anonymous Structs
tests := []struct {
input string
expected int
}{
{"hello", 5},
{"", 0},
{"go", 2},
}
for _, tt := range tests {
if got := len(tt.input); got != tt.expected {
t.Errorf("len(%q) = %d, want %d", tt.input, got, tt.expected)
}
}
Anonymous structs are idiomatic for table-driven tests and ephemeral data shapes. No need to pollute the package namespace with a type used only in one function.