In Go, what does `var s Server` give you when Server holds an int, a nested struct, a [4]byte array and a map field?
answer
- declaration zeroes the whole value
- recursion, not just the top level
- arrays get zeroed elements, maps get nil
- no default values in a field declaration
- comparing against Server{} may not compile
basics
~20 sYou get a complete Server value with every field zeroed recursively: the int is 0, each field of the nested struct is its own zero value, all four array bytes are 0, and the map field is nil with nothing allocated for it.
solid answer
~50 s`var s Server` allocates the whole struct and zeroes it, and the zeroing is recursive rather than one level deep. The `int` field is `0`. The nested struct field is a real struct whose own fields are each zeroed, not a nil pointer. The `[4]byte` array field holds four bytes, all `0`, stored inline in the struct. The map field is `nil` — the language allocates no hash table for it, so it must be initialised before anything is written into it. Two consequences follow. First, Go has no field default values: a declaration only zeroes memory, so any other starting value has to come from a constructor function or a composite literal at the call site. Second, a struct containing a map, slice or function field is not comparable, so you cannot test `s == Server{}` to ask whether the value is still untouched — that line will not compile.
code
go · 15 linestype Limits struct {
MaxConns int
Prefix [4]byte
Tags map[string]string
}
type Server struct {
Name string
Limits Limits
Ready bool
}
var s Server
fmt.Println(s.Name == "", s.Limits.MaxConns, s.Limits.Prefix[0], s.Limits.Tags == nil)
// true 0 0 truego deeper
Know that declaring a struct variable gives you a real struct value, not a nil pointer, and that each field starts at its own type's zero value.
Be able to walk the fields one by one and say why the array holds zeroed elements while the map field is nil, and explain why Go has no default values in a field declaration.
Point out the half-usable type: a struct whose zero value reads fine but panics on the first write to a nil map field, and say how you would design that field away or initialise it.
Set the house rule for types other teams embed: either the zero value is fully usable and documented as such, or the type is only ever built by a constructor — mixing the two is what produces surprise panics in code you never see.
## What the declaration actually does `var s Server` reserves memory for one whole `Server` value and sets it to the zero value of the type. For a struct, the zero value is defined as the value whose every field is that field's own zero value — applied recursively, all the way down. There is no constructor call, no per-field initialiser, and no hidden allocation for the fields. Take a concrete shape: ```go type Limits struct { MaxConns int Prefix [4]byte Tags map[string]string } type Server struct { Name string Limits Limits Ready bool } ``` After `var s Server`: - `s.Name` is the empty string. - `s.Ready` is `false`. - `s.Limits` is a real `Limits` value, not a pointer and not nil. Its own fields are zeroed in turn. - `s.Limits.MaxConns` is `0`. - `s.Limits.Prefix` is `[4]byte` with all four bytes `0` — the array's storage lives inside the struct, so zeroing the struct zeroes the array elements. - `s.Limits.Tags` is a nil map. No hash table exists behind it. ## Value, not pointer The most common misreading is that `var s Server` gives you something nil that you must build before touching. It does not. `s` is a complete value you can read from and assign to immediately; only fields whose own zero value is nil — the map here, and any slice, pointer, channel, function or interface fields — are nil, and only because that is what *their* zero value is. Because the struct is a value, the size of `Server` includes the four bytes of `Prefix` inline. A slice field would instead be a small fixed-size descriptor whose zero value is nil, with no backing array anywhere. That is the practical difference between an array field and a slice field at zero: the array's elements exist and are zeroed; the slice's do not exist. ## Consequence one: no default field values Go deliberately has no syntax for giving a struct field a default in its declaration. Declaration is defined as zeroing memory, not as running code, so a field can only start at its type's zero value. Any other starting point must be produced by something that executes: a `NewServer` constructor that fills the fields, a composite literal at the call site, or code that applies defaults lazily when the value is first used. This is why Go libraries either publish constructors or, more idiomatically, arrange their types so the zero value is already the right default. ## Consequence two: comparability Go lets you compare two struct values with `==` only when every field type is itself comparable. Numbers, strings, bools, pointers, channels, interfaces, comparable structs and arrays of comparable element types are comparable; **maps, slices and function values are not**. So with the `Server` above, `s == Server{}` is a compile-time error, because `Limits.Tags` is a map. That matters in practice, because "is this value still untouched?" is a question people reach for. Your options are to compare the fields you care about individually, to keep the non-comparable fields out of the struct you want to compare, or to use `reflect.DeepEqual` and accept its cost and its looser semantics. If the struct *is* fully comparable, `s == Server{}` is a legitimate and cheap zero test. ## Where this bites in real code A struct whose zero value is mostly usable but has one nil map field is a classic half-usable type: reads of the map work fine and return the element type's zero value, while the first write panics. If you own the type, you either initialise that map in a constructor, or you keep the field unexported and initialise it lazily inside the method that writes to it, or you redesign so no map is needed until the caller supplies one. A struct embedded by value inside another is zeroed with its parent, so an embedded `sync.Mutex` field is a usable unlocked mutex in the parent's zero value with no work from you — one of the reasons Go code puts the lock right next to the data it guards. ## Summary Declaration zeroes the entire value recursively. Nested structs and arrays are real, zeroed storage; nilable fields are nil. There are no field defaults, so anything else needs code; and whether you can spell the zero test as `== T{}` depends on whether every field type is comparable.
- Can you check whether a struct is still at its zero value with `s == Server{}`?Only if every field type is comparable. Numbers, strings, bools, pointers, channels, interfaces, arrays of comparable types and comparable structs all support `==`, so the test compiles and is cheap. Add a map, slice or function field and the struct becomes non-comparable, and `s == Server{}` is a compile-time error. Then you compare the fields you care about by hand, or fall back to `reflect.DeepEqual`.
- Why can't a Go struct field declare a default value?Because a declaration is defined as zeroing memory, not as running code. A field can only begin at its type's zero value, so any other starting point must come from something that executes: a constructor function that fills the fields, a composite literal written by the caller, or defaults applied lazily at the point of use. Go's preferred answer is to design the type so the zero value is already the default you wanted.
- Does a [4]byte field make the struct bigger than a slice field would?The array's four bytes are stored inline in the struct, so yes, they are part of the struct's own memory and are zeroed with it. A slice field instead stores a small fixed-size descriptor whose zero value is nil, with no backing array allocated at all. So the array field costs storage immediately, while the slice field costs nothing until something appends to it.
saying these in an interview costs you the question
- Says the nested struct field starts out nil
- Thinks the [4]byte field is nil like a slice
- Believes the map field is an empty usable map
- Claims Go zeroes only the outermost struct level
- Assumes s == Server{} always compiles