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

от автора

Наверное, многим знакомо — пишешь ты на языке (вот скажем, 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.

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


ссылка на оригинал статьи https://habr.com/ru/articles/945448/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *