golang

Несколько занятных вопросов собеседований по Go

  • четверг, 11 сентября 2025 г. в 00:00:08
https://habr.com/ru/articles/945448/

Наверное, многим знакомо - пишешь ты на языке (вот скажем, Go) уже не первый год - а на собеседовании всё равно найдут чем удивить :) Вот поделюсь уловом последних дней - не смейтесь над моей наивностью - вдруг кому пригодится!

Суета вокруг defer-а

Дефером мы часто пользуемся (ну хотя бы чтобы мьютексы разлочить) но немного запутать им всё-таки можно. Что напечатает такой код?

package main

func pipa(x int) {
    println("pipa", x)
}

func main() {
    a := 3
    defer pipa(a)
    defer func() {
        println("fufu", a)
    }()
    a += 2
    println("main", a)
}

Думаю этим вряд ли кого-то из читателей Хабра подловить можно. Подсказкой может служить вопрос (простой) - когда вычисляются аргументы для defer-red функции.

Паника в горутине

Если в горутине возникнет паника - что произойдёт? Вот и весь вопрос, но для точности пусть будет и пояснение в виде кода - что он напечатает?

package main

import "time"

func main() {
	go func() {
		panic("Ah, Oh!")
	}()
	time.Sleep(100 * time.Millisecond)
	println("Hi Friend!")
}

Тут два основных варианта ответа - нужно просто быть в курсе. Проведя опрос среди коллег я убедился что 2/3 отвечают правильно - но всё же логика авторов языка тут неочевидна :-)

Что ты такое interface{}

Просят сказать что напечатает, код в котором есть переменная указатель и переменная any:

package main

func main() {
	var s *string
	var i interface{}
	println(s == nil)
	println(i == nil)
	i = s
	println(i == nil)
}

принты показывают что обе переменные равны nil но когда присвоишь одну другой, уже не nil - почему? Можно для размышления распечатать значения самих переменных - как подсказал интервьюер тут полезно понять что же такое переменная типа interface{} :)

Закрытый канал

Что будет при попытке прочесть из закрытого канала? Паника? Дефолтное значение? А если в нём есть данные (буферизированные, непрочитанные)? А как определить без паники что канал закрыт? Если вам приходится достаточно часто каналами пользоваться, скорее всего вы посмеётесь над лёгкостью вопроса. А тем кто не уверен предложим потестировать этот код для проверки:

package main

func main() {
	c := make(chan string, 3)
	c <- "blaha"
	c <- "muha"
	close(c)
	for {
		s, ok := <-c
		if !ok {
			break
		}
		println("msg: " + s)
	}
}

Всё верно, буфер можно вычитать, а ok оповестит что дальше канал закрыт и ждать там нечего.

Опять присваивание "к интерфейсу"

Немного похоже на "фокус с интерфейсом" выше. Или не похоже? А может одно и то же?

Что выведет этот код:

package main

type Animal struct {
	voice string
}

func (a Animal) MakeNoise() {
	println(a.voice)
}

type NoiseMaker interface {
	MakeNoise()
}

func main() {
	bob := Animal{"meow"}
	var nm NoiseMaker = bob
	bob.voice = "wof!"
	nm.MakeNoise()
}

Очевидно что-то где-то копируется вопреки "задумке". Но в какой именно момент?

Множественная аггрегация

Структура включает в себе две меньших - и для них обеих определён один и тот же метод. Какой вызовется - первый или второй?

package main

type A struct {
	v int
}

func (a A) sqr() int {
	return a.v * a.v
}

type B struct {
}

func (b B) sqr() int {
	return 13
}

type C struct {
	A
	B
}

func main() {
	c := C{A{13}, B{}}
	println(c.sqr())
}

А может будет ошибка компиляции, или паника? Если вы хоть раз с этим сталкивались, ответ, конечно, дать легко :-)

Что вы помните про Go Memory Model

Если вы видели этот небольшой мануал - он на самом деле не столько про организацию памяти, сколько про разрешение рейс-кондишнов, необходимость синхронизации и т.п. В общем-то очень важные и нужные вещи...

Но предваряет этот длинный текст короткий список с пометкой Advice - и я всегда с радостью говорю что лучше всего мне запомнился последний (кажется, четвёртый) совет из этого списка.

Don't be clever.

Согласитесь, звучит замечательно и по-философски, хоть в контексте, хоть без - я адресую это авторам всех хитроумных вопросов на собеседованиях :-)