Irbisa · Cheatsheet

Structs, Methods & Interfaces

Structs, embedding, method sets, implicit interfaces, type switches

Junior Developer · 24 questions

  1. 01

    How does Go do OOP without classes: what does it use for encapsulation, inheritance and polymorphism?

    Go has no classes and no inheritance.

  2. 02

    What is the difference between a keyed struct literal T{Name: v} and a positional one T{v1, v2}, and which should you write?

    A keyed literal names the fields it sets and leaves the rest at their zero value; a positional one must list every field in declaration order.

    package geo import "net" type Point struct{ X, Y int }…
  3. 03

    Why can't you add a method to time.Time or int, and what do you do instead?

    A method can only be declared on a named type defined in the same package, so time.Time and int are closed to you.

    package temp import (	"fmt"	"time")…
  4. 04

    Which structs can you compare with == or use as a map key, and what changes when you add a []string field?

    A struct is comparable only if every one of its fields is comparable, so one slice, map or func field takes == and map-key use away from the whole struct, and the compiler tells you so.

    package keys type Point struct{ X, Y int } type Loose struct{ Tags []string }…
  5. 05

    What is the empty struct struct{} used for in Go?

    struct{} has no fields and takes zero bytes, so it is the type for values that carry no data: set members in map[K]struct{}, signals on chan struct{}, and types that exist only for their methods.

    package sig import "fmt" type noopLogger struct{} // no state, only methods…
  6. 06

    What is an interface in Go, and how does implicit satisfaction differ from implements in Java?

    An interface is a set of method signatures, and any type that has those methods satisfies it automatically; there is no implements declaration.

    package shapes import (	"fmt"	"math")…
  7. 07

    What is the empty interface any, when is it the right type, and when should you use generics instead?

    any is an alias for interface{}, the interface with no methods, so every type satisfies it.

    package pick import "cmp" // any: the caller gets any back and must assert; nothing is checked.func MaxAny(xs []any) any {…
  8. 08

    What is the difference between a type assertion v.(T) and a type switch, and what happens when an assertion fails with and without ok?

    A type assertion checks for one type: s := v.(string) panics if the value inside is not a string, while s, ok := v.(string) gives the zero value and false.

    package tsw import (	"fmt"	"net/http")…
  9. 09

    What is fmt.Stringer, and why does fmt.Sprintf("%v", u) inside func (u User) String() string overflow the stack?

    fmt.Stringer is the one-method interface String() string, and fmt calls it for %v, %s and Println.

    package user import "fmt" type User struct {	Name  string…
  10. 10

    When do you give a method a pointer receiver instead of a value receiver?

    Use a pointer receiver when the method changes the receiver, when the struct holds something that must not be copied such as a sync.Mutex, or when copying it is measurably expensive.

    package counter type Counter struct{ n int } // wrong: the receiver is a copy; the caller's Counter never changes.func (c Counter) IncBroken() { c.n++ }…
  11. 11

    What is a method set, and why does Worker{} not satisfy an interface whose Run method has a pointer receiver?

    The method set of T holds only its value-receiver methods; the method set of *T holds both kinds.

    package work type Worker struct{ done int } func (w *Worker) Run()     { w.done++ }      // in the method set of *Worker onlyfunc (w Worker) Done() int { return w.done } // in both method sets…
  12. 12

    Go has no constructors. What is the NewT convention, and when do you reach for functional options?

    A constructor is an ordinary function, by convention NewT (or New when the package has one main type), that validates input, fills defaults and returns the value, often with an error.

    package server import (	"errors"	"log/slog"	"time"…
  13. 13

    What does this program print, and why doesn't Outer.Name override the Name that Describe calls?

    type Base struct{} func (Base) Name() string       { return "base" }func (b Base) Describe() string { return "I am " + b.Name() } type Outer struct{ Base } func (Outer) Name() string { return "outer" } func main() {	o := Outer{}	fmt.Println(o.Name())	fmt.Println(o.Describe())	fmt.Println(o.Base.Name())}

    It prints outer, I am base, base.

  14. 14

    What does embedding an interface in a struct do, and when is it useful?

    The struct gets every method of the interface, forwarded to whatever value the field holds, so it satisfies the interface and you redefine only the methods you want to change.

    package mw import (	"log/slog"	"net/http"	"sort"…
  15. 15

    Find the bug: the audit log written by logEvent holds only timestamps; name and user_id never appear.

    type Event struct {	time.Time	Name   string `json:"name"`	UserID int64  `json:"user_id"`} func logEvent(w io.Writer, e Event) error {	b, err := json.Marshal(e)	if err != nil {		return err	}	_, err = w.Write(append(b, '\n'))	return err}

    Embedding time.Time promotes its MarshalJSON to Event, so Event itself implements json.Marshaler and json.Marshal writes only the timestamp, "2026-10-15T09:00:00Z".

  16. 16

    What is a struct tag, how do packages like encoding/json read it, and what happens when a tag has a typo?

    A struct tag is a string attached to a field, kept in the type's metadata and read at run time through reflect.StructField.Tag.

    package api import (	"encoding/json"	"fmt"	"reflect"…
  17. 17

    Go has no implements keyword. How do you make the compiler check that a type satisfies an interface right where the type is defined?

    Add var _ Store = (*PostgresStore)(nil) next to the type.

    package store import "context" type Store interface {	Get(ctx context.Context, key string) ([]byte, error)…
  18. 18

    Where should an interface be declared in Go, and what does "accept interfaces, return structs" mean?

    Declare an interface in the package that uses it, with only the methods that package calls, and have constructors return concrete types.

  19. 19

    What does this code print, and why does calling a method on a nil pointer not panic?

    type Tree struct {	Left, Right *Tree	Val         int} func (t *Tree) Sum() int {	if t == nil {		return 0	}	return t.Val + t.Left.Sum() + t.Right.Sum()} func main() {	var t *Tree	fmt.Println(t.Sum())	fmt.Println((&Tree{Val: 1, Left: &Tree{Val: 2}}).Sum())}

    It prints 0 and then 3.

  20. 20

    This code prints false and then panics. Why is w not nil, and how do you fix auditLog?

    func auditLog(enabled bool) io.Writer {	var buf *bytes.Buffer	if enabled {		buf = new(bytes.Buffer)	}	return buf} func main() {	w := auditLog(false)	fmt.Println(w == nil)	w.Write([]byte("x"))}

    An interface value holds two things (the dynamic type and a pointer to the value) and equals nil only when both are empty.

  21. 21

    When are two interface values equal under ==, and when does that comparison panic at run time?

    Two interface values are equal when both are nil, or when they hold the same dynamic type and equal values.

    package ifeq import (	"errors"	"fmt")…
  22. 22

    Find the bug: under load the service using this Cache sometimes crashes with concurrent map read and map write and sometimes hangs.

    type Cache struct {	mu sync.Mutex	m  map[string]string} func (c *Cache) Set(k, v string) {	c.mu.Lock()	defer c.mu.Unlock()	c.m[k] = v} func (c Cache) Get(k string) string {	c.mu.Lock()	defer c.mu.Unlock()	return c.m[k]}

    Get has a value receiver, so each call copies the whole Cache, mutex included, and locks the copy.

  23. 23

    How is an interface value represented at run time (eface, iface, itab), and what does putting a value into an interface cost?

    An interface value is two machine words: a type word and a data pointer.

    // Simplified from runtime/runtime2.go and internal/abi (Go 1.25).package sketch import "unsafe" type _type struct{}         // size, hash, kind, GC data...…
  24. 24

    Two structs with the same four fields measure 24 and 16 bytes under unsafe.Sizeof. What decides struct layout, and when is reordering fields worth it?

    Fields are placed in declaration order, each at an offset that is a multiple of its own alignment.

    package layout import (	"fmt"	"unsafe")…