A struct is just a way to group related fields under one type. Think of it like a record or a row — a bundle of named values that belong together.
type User struct {
Name string
Age int
}
Creating them: literals
There are a few ways to make a struct value. The named-field form is the one we should prefer because it survives field reordering.
u1 := User{Name: "Manish", Age: 27} // named fields — clearest
u2 := User{"Aisha", 30} // positional — fragile, order matters
u3 := User{Name: "Bob"} // Age defaults to its zero value (0)
fmt.Println(u1, u2, u3)
Anonymous structs
Sometimes we want a one-off struct without naming the type — handy for quick config or test data. We just declare the shape and the value together.
point := struct {
X, Y int
}{X: 1, Y: 2}
fmt.Println(point.X, point.Y) // 1 2
Struct tags (json)
Tags are little strings of metadata attached to fields. The encoding/json package reads them to decide JSON key names. Without a tag, JSON uses the field name as-is.
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price,omitempty"` // omitempty: skip if zero value
}
// marshals to: {"id":1,"name":"Pen"} (price dropped when 0)
The omitempty part means “leave this key out of the JSON if the value is empty/zero.” Very common in API code.
Embedding: composition over inheritance
Go has no classes and no inheritance. Instead it gives us embedding — we drop one struct inside another without a field name, and the outer struct gets the inner one’s fields and methods for free.
In simple language, embedding is “this type IS-A / HAS the abilities of that type” by composing them, not by subclassing.
type Animal struct {
Name string
}
func (a Animal) Speak() string { // method on Animal
return a.Name + " makes a sound"
}
type Dog struct {
Animal // embedded — note: NO field name
Breed string
}
Method promotion
Because Animal is embedded, the Dog automatically “promotes” Animal’s fields and methods. We can call them directly on the Dog as if they were its own.
d := Dog{
Animal: Animal{Name: "Rex"},
Breed: "Lab",
}
fmt.Println(d.Name) // "Rex" — promoted field
fmt.Println(d.Speak()) // "Rex makes a sound" — promoted method
That’s the whole trick. We didn’t redeclare Name or rewrite Speak() — the embedded Animal gave them to Dog. If Dog defined its own Speak(), that would take priority (the outer one shadows the inner one), which is how we “override” behavior.
Interview soundbite
Structs group related fields, and we prefer named-field literals so reordering doesn’t break things. Go has no inheritance — instead we embed one struct inside another without a field name, and the outer type promotes the inner type’s fields and methods. That’s Go’s composition-over-inheritance, and an outer method with the same name shadows the embedded one.