Irbisa · Шпаргалка

Основы Go

Синтаксис, нулевые значения, константы и iota, control flow, defer и замыкания

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

  1. 01

    Что такое Go, зачем его создали и какие у него главные сильные и слабые стороны?

    Go представляет собой статически типизированный компилируемый язык со сборщиком мусора.

  2. 02

    Какие привычные возможности Go намеренно не поддерживает (тернарный оператор, while, наследование, исключения, перегрузку) и что использует вместо них?

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

  3. 03

    Что такое пакет в Go, как его импортировать и что делает имя экспортируемым?

    Пакет объединяет все .go-файлы одной директории с одинаковым объявлением package.

    // store/store.go, in module example.com/apppackage store type Row struct {	ID   int    // exported: visible to other packages and to encoding/json	name string // unexported: visible only inside package store…
  4. 04

    Чем отличаются go run, go build и go install?

    Все три компилируют одинаково и отличаются только тем, куда попадает бинарник: go run собирает его во временной директории, запускает и удаляет; go build кладёт его в текущую директорию (или по пути из -o); go install устанавливает его в $GOBIN, по умолчанию ~/go/bin.

  5. 05

    Какими способами объявляют переменную в Go и где можно использовать :=?

    var объявляет переменную где угодно (с типом, с начальным значением или с тем и другим); короткая форма := объявляет и выводит тип за один шаг, но работает только внутри тела функции.

    var build = "dev" // package level: `build := "dev"` here is a syntax error func load(path string) error {	var rows []Row    // no value yet: the zero value (a nil slice) is what we want	var id uint64 = 7 // := would have inferred int…
  6. 06

    Что такое нулевое значение (zero value) в Go и какое оно у чисел, строк, указателей, слайсов, map, каналов и структур?

    Нулевым значением называют то, что лежит в переменной, объявленной без инициализатора: 0 у чисел, false у bool, "" у строк, nil у указателей, слайсов, map, каналов, функций и интерфейсов.

    type Server struct {	Addr string          // ""	Port int             // 0	mu   sync.Mutex      // ready to use, no constructor	buf  bytes.Buffer    // same	tags []string        // nil…
  7. 07

    Что такое константы и iota в Go и как написать enum?

    Константой называют значение, известное на этапе компиляции: число, строка, булево значение или руна, объявленное через const.

    type State uint8 const (	StateUnknown State = iota // 0, also the zero value of State	StatePending              // 1: the line repeats `State = iota`	StateRunning              // 2…
  8. 08

    Какой размер у int в Go и что происходит при целочисленном переполнении и делении на ноль?

    int и uint занимают 64 бита на 64-битных платформах и 32 бита на 32-битных.

    func main() {	defer func() { fmt.Println("recovered:", recover()) }() 	fmt.Println(strconv.IntSize) // 64 on amd64 and arm64 	var a int8 = 127…
  9. 09

    Как в Go работает преобразование типов и почему нет неявного преобразования между числовыми типами?

    Любое преобразование пишется явно как T(x): Go никогда не преобразует числовые типы сам, даже int в int64, потому что тихие преобразования служат классическим источником переполнений и потери точности.

    done, total := 3, 4fmt.Println(done / total * 100)                   // 0: integer division firstfmt.Println(float64(done/total) * 100)            // 0: converted too latefmt.Println(float64(done) / float64(total) * 100) // 75 v, f, n := 300, 3.9, -1…
  10. 10

    Какие формы принимает цикл for в Go и по чему может итерироваться range?

    for является единственным ключевым словом цикла в Go, и у него три формы: с тремя частями for i := 0; i < n; i++, только с условием for cond {} (это и есть while) и пустой for {}, который крутится до break или return.

    for i := 0; i < 3; i++ { // three-clause	fmt.Print(i)} for rows.Next() { // condition only: Go's while	scan(rows)…
  11. 11

    Что напечатает этот цикл и как сделать так, чтобы break останавливал цикл?

    jobs := []string{"a", "stop", "b"}for _, j := range jobs {	switch j {	case "stop":		fmt.Println("stopping")		break	default:		fmt.Println("run", j)	}}

    Он напечатает run a, stopping, run b.

  12. 12

    Как в Go работают несколько возвращаемых значений и для чего нужен пустой идентификатор _?

    Функция может вернуть несколько значений, и по соглашению последнее из них имеет тип error.

    func divmod(a, b int) (int, int, error) {	if b == 0 {		return 0, 0, errors.New("division by zero")	}	return a / b, a % b, nil}…
  13. 13

    Что такое вариативная функция и как передать в неё слайс?

    Вариативная функция принимает любое число аргументов одного типа через последний параметр, как func sum(xs ...int) int; внутри функции xs оказывается обычным []int.

    func sum(xs ...int) int {	total := 0	for _, x := range xs {		total += x	}	return total…
  14. 14

    Как в Go передаются аргументы в функцию и когда передавать указатель вместо значения?

    Go всегда передаёт по значению: функция получает копию каждого аргумента.

    type Config struct {	Addr    string	Timeout time.Duration} func setTimeoutCopy(c Config) { // wrong: changes a copy…
  15. 15

    Чем отличаются make и new?

    new(T) работает с любым типом: выделяет обнулённое значение T и возвращает *T.

    p := new(int)             // *int pointing at 0u := &User{Name: "ada"}   // *User with fields set: the usual choicem := make(map[string]int) // ready for writess := make([]int, 0, 100)  // len 0, cap 100ch := make(chan int, 10)  // buffered channel…
  16. 16

    Как работает defer: когда выполняются отложенные вызовы, в каком порядке и когда вычисляются их аргументы?

    defer откладывает вызов до выхода из окружающей функции (через return или при разворачивании паники).

    func countdown() {	for i := 0; i < 3; i++ {		defer fmt.Print(i, " ") // i is evaluated now, the call runs at return	}} // prints 2 1 0…
  17. 17

    Чем отличаются fmt.Println, fmt.Printf и fmt.Sprintf и что печатают %v, %+v, %#v, %T и %q?

    fmt.Println печатает аргументы через пробел и добавляет перевод строки; fmt.Printf форматирует их по шаблону с глаголами; fmt.Sprintf форматирует так же, но возвращает строку, а не печатает её.

    type User struct {	ID   int	Name string} func main() {…
  18. 18

    Этот хелпер перебирает несколько URL и логирует каждый неудачный, но возвращает nil, даже когда все они провалились. Почему и почему компилятор молчит?

    func fetch(urls []string) error {	var err error	for _, u := range urls {		if resp, err := http.Get(u); err != nil {			log.Printf("attempt %s: %v", u, err)		} else {			resp.Body.Close()			return nil		}	}	return err}

    := в заголовке if объявляет новые resp и err, видимые только внутри этого if, поэтому внешней err ничего не присваивается, и функция возвращает её нулевое значение, то есть nil.

  19. 19

    Найдите баг: lastN возвращает верные элементы при малых n, но паникует с index out of range, когда n равно len(xs).

    func lastN(xs []int, n uint) []int {	var out []int	for i := uint(len(xs)) - 1; i >= uint(len(xs))-n; i-- {		out = append(out, xs[i])	}	return out}

    i имеет тип uint, поэтому при n == len(xs) условие i >= uint(len(xs))-n всегда истинно (справа 0); когда i доходит до 0, i-- переходит в 18446744073709551615, и xs[i] паникует.

  20. 20

    Что вернёт f() и что бы она вернула, если бы результат не был именованным?

    func f() (n int) {	defer func() {		n *= 2	}()	n = 1	return n + 2}

    Она вернёт 6: return n + 2 присваивает 3 именованному результату n, затем отложенное замыкание удваивает его, и только потом вызывающий получает n.

  21. 21

    Найдите баг: hashAll проходит тесты, но в продакшене на больших директориях падает с too many open files.

    func hashAll(paths []string) (map[string]string, error) {	sums := make(map[string]string, len(paths))	for _, p := range paths {		f, err := os.Open(p)		if err != nil {			return nil, err		}		defer f.Close()		h := sha256.New()		if _, err := io.Copy(h, f); err != nil {			return nil, err		}		sums[p] = hex.EncodeToString(h.Sum(nil))	}	return sums, nil}

    defer f.Close() выполняется, когда возвращается hashAll, а не в конце каждой итерации, поэтому все файлы остаются открытыми до конца цикла; при достаточном числе путей процессу не хватает файловых дескрипторов, и os.Open падает с ошибкой.

  22. 22

    Что такое замыкание в Go и как Go 1.22 изменил то, что замыкания захватывают внутри цикла for?

    Замыканием называют функциональный литерал, который использует переменные окружающей функции; он захватывает сами переменные, а не их значения.

    func counter() func() int {	n := 0 // captured by the closure, outlives counter()	return func() int {		n++		return n	}…
  23. 23

    Когда выполняется init() относительно импортов и переменных уровня пакета и почему не стоит класть в него настоящую работу?

    init() выполняется один раз на пакет: после инициализации импортируемых пакетов и собственных переменных пакета и до main.

    // lookup/lookup.gopackage lookup var a = b + 1 // dependency order, not source order: b is set first, so a is 3var b = 2…
  24. 24

    Чем отличается определение типа type UserID string от алиаса типа type UserID = string?

    type UserID string представляет собой определение типа: новый, отдельный тип с базовым типом string и без собственных методов.

    type UserID string // a new typetype Name = string // an alias: Name and string are the same type func (id UserID) Valid() bool { return strings.HasPrefix(string(id), "u_") } type Set[T comparable] = map[T]struct{} // Go 1.24: generic alias…
  25. 25

    Что изменилось в Go за последние несколько релизов и какие из этих изменений влияют на повседневный код?

    Для повседневного кода главное: своя переменная цикла на каждой итерации и range по целым числам (1.22), итераторы (1.23), дженерик-алиасы и map на Swiss-таблицах (1.24), GOMAXPROCS с учётом лимитов контейнера (1.25), new(expr) и сборщик мусора Green Tea по умолчанию (1.26), а также дженерик-методы (1.27).