Irbisa · Шпаргалка

Структуры, методы и интерфейсы

Структуры, встраивание, наборы методов, неявные интерфейсы, type switch

Junior-разработчик · 24 вопроса

  1. 01

    Как в Go устроено ООП без классов: что используется для инкапсуляции, наследования и полиморфизма?

    В Go нет классов и нет наследования.

  2. 02

    Чем литерал структуры с именами полей T{Name: v} отличается от позиционного T{v1, v2} и какой из них стоит писать?

    Литерал с именами называет поля, которые задаёт, а остальные оставляет с нулевыми значениями; позиционный обязан перечислить все поля в порядке объявления.

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

    Почему нельзя добавить метод к time.Time или int и что делать вместо этого?

    Метод можно объявить только на именованном типе, определённом в том же пакете, поэтому time.Time и int для вас закрыты.

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

    Какие структуры можно сравнивать через == и использовать как ключ map и что меняет добавленное поле []string?

    Структура сравнима, только если сравнимо каждое её поле, поэтому одно поле-slice, map или func отбирает у всей структуры и ==, и роль ключа map, и компилятор об этом скажет.

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

    Для чего в Go используют пустую структуру struct{}?

    У struct{} нет полей и она занимает ноль байт, поэтому это тип для значений, которые не несут данных: элементы множества в map[K]struct{}, сигналы в chan struct{} и типы, которые существуют ради методов.

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

    Что такое интерфейс в Go и чем неявное соответствие отличается от implements в Java?

    Интерфейс задаёт набор сигнатур методов, и любой тип, у которого эти методы есть, соответствует ему автоматически: объявления implements нет.

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

    Что такое пустой интерфейс any, когда это правильный тип и когда вместо него нужны дженерики?

    any является псевдонимом interface{}, интерфейса без методов, поэтому ему соответствует любой тип.

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

    Чем утверждение типа v.(T) отличается от type switch и что происходит при неудачном утверждении с ok и без него?

    Утверждение типа проверяет один тип: s := v.(string) паникует, если внутри не string, а s, ok := v.(string) возвращает нулевое значение и false.

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

    Что такое fmt.Stringer и почему fmt.Sprintf("%v", u) внутри func (u User) String() string переполняет стек?

    fmt.Stringer объявлен как интерфейс из одного метода String() string, и fmt вызывает его для %v, %s и Println.

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

    Когда методу нужен указательный получатель, а не получатель-значение?

    Указательный получатель нужен, когда метод меняет получателя, когда в структуре есть то, что нельзя копировать, например sync.Mutex, или когда копирование заметно дорого.

    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

    Что такое набор методов и почему Worker{} не соответствует интерфейсу, если у метода Run указательный получатель?

    В набор методов T входят только методы с получателем-значением; в набор методов *T входят оба вида.

    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 нет конструкторов. Что такое соглашение NewT и когда нужны функциональные опции?

    Конструктор представляет собой обычную функцию, по соглашению NewT (или New, если в пакете один главный тип), которая проверяет входные данные, заполняет значения по умолчанию и возвращает значение, обычно вместе с error.

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

    Что напечатает эта программа и почему Outer.Name не переопределяет Name, который вызывает Describe?

    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())}

    Она печатает outer, I am base, base.

  14. 14

    Что даёт встраивание интерфейса в структуру и когда это полезно?

    Структура получает все методы интерфейса, переадресованные значению, которое лежит в поле, поэтому она реализует интерфейс, а вы переопределяете только те методы, которые хотите изменить.

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

    Найдите баг: в журнале аудита, который пишет logEvent, только временные метки, а name и user_id там не появляются.

    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}

    Встраивание time.Time продвигает его MarshalJSON в Event, поэтому сам Event реализует json.Marshaler, и json.Marshal пишет только время: "2026-10-15T09:00:00Z".

  16. 16

    Что такое struct tag, как его читают пакеты вроде encoding/json и что будет, если в теге опечатка?

    Struct tag представляет собой строку, привязанную к полю, которая хранится в метаданных типа и читается в рантайме через reflect.StructField.Tag.

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

    В Go нет ключевого слова implements. Как заставить компилятор проверить соответствие типа интерфейсу прямо там, где тип объявлен?

    Добавьте рядом с типом var _ Store = (*PostgresStore)(nil).

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

    Где в Go объявлять интерфейс и что значит «принимай интерфейсы, возвращай структуры»?

    Объявляйте интерфейс в пакете, который его использует, и только с теми методами, которые этот пакет вызывает, а конструкторы пусть возвращают конкретные типы.

  19. 19

    Что напечатает этот код и почему вызов метода на nil-указателе не паникует?

    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())}

    Он печатает 0, а затем 3.

  20. 20

    Этот код печатает false, а затем паникует. Почему w не nil и как исправить 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"))}

    Значение интерфейса хранит две вещи (динамический тип и указатель на значение) и равно nil, только когда пусты обе.

  21. 21

    Когда два значения интерфейса равны по == и когда такое сравнение паникует в рантайме?

    Два значения интерфейса равны, когда оба nil или когда в них лежат один и тот же динамический тип и равные значения.

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

    Найдите баг: под нагрузкой сервис с этим Cache то падает с concurrent map read and map write, то зависает.

    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 получатель по значению, поэтому каждый вызов копирует весь Cache вместе с мьютексом и блокирует копию.

  23. 23

    Как значение интерфейса представлено в рантайме (eface, iface, itab) и во что обходится положить в интерфейс значение?

    Значение интерфейса занимает два машинных слова: слово типа и указатель на данные.

    // 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

    Две структуры с одинаковыми четырьмя полями дают 24 и 16 байт под unsafe.Sizeof. Что определяет раскладку структуры и когда стоит переставлять поля?

    Поля кладутся в порядке объявления, каждое по смещению, кратному его собственному выравниванию.

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