Наверное, многим знакомо - пишешь ты на языке (вот скажем, 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.
Согласитесь, звучит замечательно и по-философски, хоть в контексте, хоть без - я адресую это авторам всех хитроумных вопросов на собеседованиях :-)
Octagon77
Служить не может, ибо спрашивают, скорее всего, порядок выполнения ибо это нужно тупо знать. Вариант Go логичней с точки зрения реализации, альтернатива - может быть удобней для чтения.
Логика именно что очевидна. Альтернатива приводит к возникновнию минимуо одной новой сущности, типа специальной "паники в горутине", и открывает следующий вопрос - а не сделать ли по-разному в зависомости от того, в каком потоке ОС горутина выполняется. А если вспомнить, что ограниченный до отсутствия доступ к планировщику - стратегическое решение, то вдвойне очевидна.
Какая душка этот интервьюер... Я бы сказал жёстче и в морду. А теперь вот задумался - если бы, как по моему, изгнать всех кто делал из себя нейросеть обучаясь на примерах - останется кто или уже кранты...
Ой, пропаганда упомянутого выше метода обучения. А почему было не попросить исправить код и узнать даже больше - я не понимаю.
RodionGork Автор
что вам мешает написать собственную статью на свой вкус вместо пространных брюзжащих комментов - я тоже не понимаю :)
да ну, это ваше личное восприятие. если бы это было логично то было бы не менее логично чтобы main дожидался окончания работы всех запущенных горутин.
единственная внятная логика в том что асинхронно запущенная процедура должна быть асинхронной и максимально независимой от остальных. всё остальное - ересь.
там кроме порядка выполнения как раз и порядок вычисления аргументов же задействован. нагляднее было бы только использовать в аргументе функцию с сайд-эффектом но это уж слишком толсто