Несколько занятных вопросов собеседований по Go
- четверг, 11 сентября 2025 г. в 00:00:08
Наверное, многим знакомо - пишешь ты на языке (вот скажем, Go) уже не первый год - а на собеседовании всё равно найдут чем удивить :) Вот поделюсь уловом последних дней - не смейтесь над моей наивностью - вдруг кому пригодится!
Дефером мы часто пользуемся (ну хотя бы чтобы мьютексы разлочить) но немного запутать им всё-таки можно. Что напечатает такой код?
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 отвечают правильно - но всё же логика авторов языка тут неочевидна :-)
Просят сказать что напечатает, код в котором есть переменная указатель и переменная 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())
}
А может будет ошибка компиляции, или паника? Если вы хоть раз с этим сталкивались, ответ, конечно, дать легко :-)
Если вы видели этот небольшой мануал - он на самом деле не столько про организацию памяти, сколько про разрешение рейс-кондишнов, необходимость синхронизации и т.п. В общем-то очень важные и нужные вещи...
Но предваряет этот длинный текст короткий список с пометкой Advice - и я всегда с радостью говорю что лучше всего мне запомнился последний (кажется, четвёртый) совет из этого списка.
Don't be clever.
Согласитесь, звучит замечательно и по-философски, хоть в контексте, хоть без - я адресую это авторам всех хитроумных вопросов на собеседованиях :-)