📕
Golang
  • Собеседование по golang
  • Вопросы собеседования
    • Список вопросов МТС
    • Список вопросов
    • Базовые вопросы по Golang
    • Go. Прорабатываем 25 основных вопросов собеседования
    • Функции
    • Структуры данных
    • Конкурентность и Параллелизм
    • Горутины
    • Примитивы синхронизации
    • Планировщик
    • Go: конкурентность и привязки к потокам в планировщике
    • Каналы
    • GC
  • спецификация
    • Спецификация Go: преобразования в и из строкового типа
    • переключатель типов (type switch)
    • for утверждения (for statements)
    • for утверждения с range условием
    • go утверждения (go statements)
    • select утверждения (select statements)
    • return утверждения (return statements)
    • continue утверждения (continue statements)
    • goto утверждения (goto statements)
    • fallthrough утверждения (fallthrough statements, утверждения "провала")
    • defer утверждения (defer statements)
    • встроенные функции, функция close
    • длина и емкость
    • аллокация, создание срезов (slice), карт (map) и каналов
    • добавление в срезы и копирование срезов
    • удаление элементов карты
    • обработка паники
    • начальная загрузка (bootstrapping)
    • пакеты
    • инициализация и выполнение программы, нулевое значение
    • инициализация пакета
    • выполнение программы
    • ошибки
    • паника во время выполнения (run-time panic)
  • Эффективный go
    • эффективный go
    • 50 оттенков go
    • Go: распространенные антипаттерны
    • Визуализация concurrency в Go с WebGL
  • требования для работы
    • Список навыков
  • habr
    • Изучаем многопоточное программирование в Go по картинкам
    • Go: конкурентность и привязки к потокам в планировщике
  • NP
    • Полиморфизм с интерфейсами в Golang
    • Объектно-ориентированное программирование в Golang
    • Владеешь merge  -  освой и rebase
  • ProgLib
    • Горутины
  • Untitled
  • Оптимизация
    • Go: Должен ли я использовать указатель вместо копии моей структуры?
  • Полезняшки
    • Using PostgreSQL JSONB with Go
Powered by GitBook
On this page
  • Характеристики Go
  • Какие технологические преимущества экосистемы Go вы можете назвать?
  • Чем вам нравится golang?
  • Статическая или динамическая типизация? строготипизирован или нет
  • Что вас огорчает в системе типов Go?
  • Почему на Go практически не пишут расширений для других языков и динамических библиотек?
  • Go - императивный или декларативный? А в чем разница?
  • Заповеди Роба Пайка
  • Что такое type-switch?
  • Как сообщить компилятору, что наш тип реализует интерфейс?
  • Значение nil в Golang
  • Вызывает ли nil сбои в Golang?
  • Защита методов в Golang
  • Значения функций nil в Golang

Was this helpful?

  1. Вопросы собеседования

Базовые вопросы по Golang

Характеристики Go

  • Императивный

Императивное программирование — это парадигма, основанная на составлении алгоритма действий (инструкций/команд), которые изменяют состояние (информацию/данные/память) программы.

Декларативное программирование — это парадигма, при которой описывается желаемый результат, без составления детального алгоритма его получения. В пример можно привести HTML и SQL.

  • Компилируемый в нативный код

  • Статическая типизация Стати́ческая типиза́ция — приём, широко используемый в языках программирования, при котором переменная, параметр подпрограммы, возвращаемое значение функции связывается с типом в момент объявления и тип не может быть изменён позже.

  • Нет классов, но есть структуры с методами

  • Есть интерфейсы

  • Нет наследования, но есть встраивание

  • Функции - объекты первого класса. Функции первого класса - это означает, что язык поддерживает передачу функций в качестве аргументов другим функциям, возврат их как результат других функций, присваивание их переменным или сохранение в структурах данных

  • Есть замыкания Замыкание - это функция, которая ссылается к переменным вне ее тела. Функция имеет доступ к связанным переменным, а также может присваивать им значения; в этом смысле функция "связана" с этими переменными.

  • Функции могут возвращать больше 1 значения

  • Есть указатели, но нет адресной арифметики

  • Обширные возможности для конкурентности

  • Сборка в 1 бинарный файл

  • Набор стандартный инструментов

Какие технологические преимущества экосистемы Go вы можете назвать?

Прекрасная стандартная библиотека

Чем вам нравится golang?

  • Простой, надежный и продуктивный.

  • Открытый исходный код.

  • Быстрая компиляция и выполнение.

  • Простое управление зависимостями.

  • Качественно составленные полезные пакеты stdlib.

  • Простая обработка ошибок.

  • Один бинарный файл для управления всеми другими.

  • Кросс-компиляция.

  • Сборка мусора.

  • Читабельность.

  • Встроенное тестирование.

  • Профилирование.

Статическая или динамическая типизация? строготипизирован или нет

Что вас огорчает в системе типов Go?

  • Отсутствие перечислимых типов. Вместо них используются группы констант, но все константы-значения фактически являются целыми числами, группы эти синтаксически не объединены, и компилятор не может контролировать их использование. Невозможно описать тип, связанный с перечислением, например, массив, содержащий по одному элементу на каждый элемент перечисления (который в Паскале описывается конструкцией вида type EnumArray = array[EnumType] of ElementType), создать цикл по перечислению, компилятор не может контролировать полноту списка альтернатив в конструкции switch, когда в качестве селектора используется значение перечисления.

  • нет обобщённых типов (generics)

Почему на Go практически не пишут расширений для других языков и динамических библиотек?

Go - императивный или декларативный? А в чем разница?

Golang - императивный язык программирования

Императивное программирование — это парадигма, основанная на составлении алгоритма действий (инструкций/команд), которые изменяют состояние (информацию/данные/память) программы. Первыми языками программирования, основанными на таком подходе, были машинные коды и ассемблеры. Фактически, программа на этих языках — это код, который выполняется компьютером сразу, без предварительной компиляции. Из языков высокого уровня, требующих компиляции исходного кода программы в машинный код (или интерпретации), к императивным можно отнести C, C++, Java.

Декларативное программирование — это парадигма, при которой описывается желаемый результат, без составления детального алгоритма его получения. В пример можно привести HTML и SQL. При создании HTML мы с помощью тегов описываем, какую хотим получить страничку в браузере, а не то, как нарисовать на экране заголовок статьи, оглавление и текст. В SQL, если нам нужно посчитать количество сотрудников с фамилией «Сидоров», мы напишем SELECT count(*) FROM employee WHERE last_name = 'Сидоров';. Тут ничего не сказано про то, в каком файле или области памяти находятся данные по сотрудникам, как именно выбрать из них всех Сидоровых и нужно ли вообще это делать для подсчёта их количества.

Заповеди Роба Пайка

https://habr.com/ru/post/272383/

Don't communicate by sharing memory, share memory by communicating.

«Не общайтесь разделением памяти. Разделяйте память через общение.»

Concurrency is not parallelism.

«Конкурентность — это не параллелизм»

The bigger the interface, the weaker the abstraction.

Чем больше интерфейс, тем слабее абстракция

Новички в Go, особенно пришедшие с Java, часто считают, что интерфейсы должны быть большими и содержать много методов. Также часто их смущает неявное удовлетворение интерфейсов. Но самое важное в интерфейсах не это, а культура вокруг них, которая отображена в этом постулате. Чем меньше интерфейс, тем более он полезен. Пайк шутит, что три самых полезных интерфейса, которые он написал — io.Reader, io.Writer и interface{} — на троих в среднем имеют 0.666 метода.

Make the zero value useful.

Делайте нулевое значение полезным

interface{} says nothing.

Пустой интерфейс ни о чём не говорит

Этот постулат говорит о том, что интерфейсы — «поведенческие типы» — должны что-то означать. Если вы создаёте интерфейс, это что-то означает и служит конкретной цели. Пустой же интерфейс (interface{}) ничего не означает и ни о чём не говорит. Есть ситуации, когда его нужно использовать, но они чаще исключение — не используйте interface{} без повода. Новички часто переиспользуют пустые интерфейсы, и масса вопросов на Stack Overflow именно о них.

Gofmt's style is no one's favorite, yet gofmt is everyone's favorite.

Стиль Gofmt — это не чье-то предпочтение, но gofmt предпочитают все

Многие новички, борясь с привычками, жалуются на то, что код в Go отформатирован не так, как им нравится. Gofmt — это единый стиль, он не является чьим-то личным предпочтением. Даже сам Грисмайер, автор утилиты go fmt, сам предпочёл бы другой стиль форматирования. Но с того момента, как формат был утверждён и gofmt используется везде и всегда — просто примите это как должное. Опытные программисты считают go fmt спасением, так как он очень упрощает работу с чужим кодом — ад с разными стилями форматирования в других языках ушёл в прошлое.

A little copying is better than a little dependency.

Небольшое копирование лучше небольшой зависимости

Syscall must always be guarded with build tags.

Код с syscall-вызовы должен содержать build-теги

"Channels orchestrate; mutexes serialize"

Каналы — оркестрируют, мьютексы — сериализируют

Cgo is not go

Cgo это не Go

Многие радуются тому, как легко в Go использовать C-код. Иногда, это, конечно, необходимо, но Пайк признается, что сам никогда не использовал Cgo, и в мире Go есть четкое понимание памяти, стабильность, безопасность, сборка мусора… и с Cgo всё это идёт в топку. В 90% случаев в Google, когда кто-то говорит «моя программа крашнулась, помогите разобраться» — дело оказывается в Cgo.

"With the unsafe package there are no guarantees"

С пакетом unsafe нет гарантий

Clear is better than clever

Ясно лучше, чем заумно

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

Reflection is never clear

Рефлексия никогда не понятна

Errors are values

Ошибки это значения

Dont’ just check errors, handle them gracefully

Не просто проверяйте ошибки, обрабатывайте их красиво

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

"Design the architecture, name the components, document the details"

Продумывайте архитектуру, называйте компоненты, документируйте детали

Тут речь о важности правильного именования компонентов вашего кода, методов, функций и переменных. Эти имена будут потом находится в коде пользователей вашего пакета. И если имена удачны и понятны, дизайн программы будет более ясен и прост для понимания. Но, конечно, всегда есть детали, которые нужно объяснить — и вот их нужно помещать в документацию.

"Documentation is for users"

Документация — для пользователей

О том, что документация к функции должна описывать не что эта функция делает, а для чего она нужна. Это очень важное различие, которое зачастую упускается из виду. Когда пишете документацию, посмотрите на неё глазами пользователя вашего пакета.

Что такое type-switch?

switch может быть использован для определения динамических типов интерфейсных переменных. Так, типизированный switch использует синтаксис приведения типов, с ключевым словом type внутри скобок. Если switch объявляет переменную в выражении, то переменная будет иметь соответствующий тип в каждом пункте.

Переключатель типов сравнивает типы, а не значения. В остальном он аналогичен переключателю выражений. Он помечается специальным выражением-переключателем, которое имеет форму утверждения типа, используя зарезервированное слово type, а не фактический тип:

switch x.(type) {
// cases
}

Затем случаи (cases) сопоставляют фактические типы T с динамическим типом выражения x. Как и в утверждении типа, x должен иметь интерфейсный тип, и каждый неинтерфейсный тип T, указанный в случае (case), должен реализовывать тип x. Типы, перечисленные в случаях переключения типов, должны быть разными.

Вместо типа случай (case) может использовать предварительно объявленный идентификатор nil; этот случай выбирается, когда выражение в TypeSwitchGuard имеет нулевое значение интерфейса. Может быть не более одного нулевого случая.

Пример:

switch i := x.(type) {
case nil:
    printString("x is nil")                // тип i это тип x (interface{})
case int:
    printInt(i)                            // тип i это int
case float64:
    printFloat64(i)                        // тип i это float64
case func(int) float64:
    printFunction(i)                       // тип i это func(int) float64
case bool, string:
    printString("type is bool or string")  // тип i это тип x (interface{})
default:
    printString("don't know the type")     // тип i это тип x (interface{})
}

"fallthrough" утверждение запрещено в переключателе типов.

Как сообщить компилятору, что наш тип реализует интерфейс?

Неявная имплементация

Если это выглядит как утка, плавает как утка и крякает как утка, то это, вероятно, утка и есть.

В Go структура с методами будет удовлетворять интерфейсу просто самим фактом объявления метода. Это кажется не особо важным на маленьких программах или искусственных примерах, но оказывается ключевым в больших проектах, где приходится думать дважды перед тем как изменить какой-то класс, многократно унаследованный другими классами. Возможность легко и просто неявно реализовать различные интерфейсы позволяет программам безболезненно расти, без необходимости продумывать все возможные интерфейсы наперёд и не утопать во множественном наследовании. Это к слову о том, что Go задумывался для облегчения жизни в больших проектах. Важное и не сразу очевидное различие этого заключается в том, как, в итоге, вы строите архитектуру вашей программы — в Java или C++ вы, скорее всего, начинаете с объявления абстрактных классов и интерфейсов, и далее переходите к конкретным реализациям. В Go же наоборот — вы пишете сначала конкретный тип, определяете данные и методы, и только в том случае, если действительно появляется необходимость абстрагировать поведение — создаете отдельный интерфейс. Опять же, масштаб этого различия более ощутим на больших проектах.

Значение nil в Golang

На заметку: В 1978 Тони Хоар также впервые описал принцип взаимодействующих последовательных процессов, или «communicating sequential processes», CSP. Его идеи лежат в основе конкурентности Go.

Однако, пока вычисления не закончены, куда должны указывать указатели? Это тот случай, когда пригодится nil. Nil может значить ближайшую звезду, пока реальная звезда не будет найдена.

В какой еще ситуации указатель в никуда может быть полезен?

Вызывает ли nil сбои в Golang?

Если указатель никуда не указывает, попытка разыменования указателя не сработает, что показано в Листинге 1. Разыменование указателя nil приведет к сбою программы. Обычно пользователям такое совсем не нравится.

Я называю это моей ошибкой в миллиард долларов.

Тони Хоар

Листинг

12345

var nowhere *int if nowhere != nil { fmt.Println(*nowhere)}

По правде говоря, сбой в программе может стать следствием многих причин, не только разыменования указателя nil. К примеру, деление на ноль также приведет к сбою, и решение проблемы будет схожим. Даже так, подумайте обо всех программах, написанных в течение последних 50 лет, количество случайных разыменований указателя nil должен быть весьма значительным.

Существование nil обременяет программиста необходимостью принятия дополнительных решений. Должен ли код проверять наличие nil, и если да, то где? Что код должен делать, если какое-то значение равно nil? После всего вышесказанного, так уж ли плох nil?

Вовсе не обязательно постоянно пытаться избегать nil. По правде говоря, в некоторых случаях nil весьма полезен. В дополнение ко всему указатели nil в Go не так популярны, как указатели null в некоторых других языках, и есть способы избежать их использования в случае нужды.

Вопрос для проверки:

Каким будет нулевое значения типа *string?Ответ

Защита методов в Golang

1234567891011121314

type person struct { age int} func (p *person) birthday() { p.age++ // разыменование указателя nil} func main() { var nobody *person fmt.Println(nobody) // Выводит: <nil> nobody.birthday()}

Скорее всего, сбой вызван после выполнения строки p.age++. Удалите данную строку, тогда программа запустится.

На заметку: Сравните это с аналогичной программой в Java, где приемник null приведет к сбою программы сразу после вызова метода.

Go вызывает методы даже в том случае, если у приемника значение nil. Приемник nil ведет себя так же, как и параметр nil. Это значит, что методы могут защищать от значений nil, как показано в следующем примере.Листинг 4Gofunc (p *person) birthday() { if p == nil { return } p.age++ }

123456

func (p *person) birthday() { if p == nil { return } p.age++}

Вместо проверки на наличие nil перед вызовом метода birthday предыдущий листинг защищает от приемников nil внутри метода.

На заметку: В Objective-C автоматический запуск метода для nil не приводит к сбою, но при вызове метода будет возвращаться нулевое значение.

С тем, как управлять nil в Go, разобрались. Методы могут возвращать нулевые значения, возвращать ошибки или приводить к сбою.

Вопрос для проверки:

Что делает доступ к полю (p.age), если p является nil?Ответ

Значения функций nil в Golang

PreviousСписок вопросовNextGo. Прорабатываем 25 основных вопросов собеседования

Last updated 4 years ago

Was this helpful?

Go действительно великолепна, особенно применительно к разработке сетевых протоколов или API: в ней есть HTTP-клиент и сервер, шифрование, форматы архивирования, сжатие, отправка писем и так далее. Есть даже парсер HTML и довольно мощный движок шаблонов, что позволяет создавать текст и HTML с автоматическим экранированием (automatic escaping) для защиты от XSS (к примеру, используется в ).

Go — язык со строгой . Доступен автоматический вывод типов. Для пользовательских типов используется «».

В и Зиллес ( Liskov and Zilles) назвали сильно типизированными те языки, в которых «при передаче объекта из вызывающей функции в вызываемую тип этого объекта должен быть совместим с типом, определённым в вызываемой функции».

Недостаточность встроенных контейнерных типов данных. Встроенные в язык контейнеры ограничиваются массивами и отображениями, а контейнеры, реализуемые средствами самого языка (в том числе входящие в стандартную библиотеку), из-за вынужденного использования в них элементов типа interface{}. К тому же их невозможно обходить с помощью конструкции for range.

Отсутствие явного указания на реализацию интерфейса типом затрудняет понимание кода, его модификацию и . Компилятор не может автоматически проверить тип на соответствие реализуемым интерфейсам. Также возможна (хотя и маловероятна) «случайная реализация» интерфейса, когда методы типа совпадают по сигнатурам с методами интерфейса, но по смыслу не являются реализацией представляемого интерфейсом поведения.

Этот постулат знают все, он звучал неоднократно в и посвященным паттернам конкурентного программирования в Go. Этот постулат призван дать понимание сути механизма работы каналов и горутин — ваши функции не делят одну и ту же память, они безопасно общаются, передавая данные по каналам.

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

Речь о том, что лучше делать нулевое значение типов полезным безо всяких конструкторов. Конечно, иногда функции-конструкторы необходимы, но если есть возможность сделать переменную вашего типа рабочей «прямо из коробки» — это всегда идёт на пользу. Пример — тип или из стандартной библиотеки. Пустое значение — это готовый к использованию буфер, хотя и существуют конструкторы вроде bytes.NewBuffer и bytes.NewBufferString().

Примечательно, что новые языки перенимают этот подход (к примеру,) — это то, о чём говорил Dave Cheney в своем — новые языки больше не будут считаться полноценными без единого стиля форматирования и утилиты, обеспечивающей это.

Это уже относится к программированию в целом, но в Go это видно и по стандартной библиотеке, и в культуре в целом. Отчасти это перекликается с известной установкой Пайк рассказывает, как в первые дни в Google ему сказали, что больше всего пекутся о реиспользовании кода. Если можно избежать написания одной повторяющейся строчки, сделав импорт — нужно так и поступать. По мнению Пайка, Google до сих пор разгребает проблемы с кодом из-за этого подхода. Очень часто функционал может быть похож (до первого рефакторинга), или необходима лишь часть функционала из зависимости, и зачастую бывает эффективней написать 5 строчек, чем импортировать 150кб зависимостей. Как пример — функция из пакета strconv — она дублирует функционал unicode.IsPrint(), при этом тесты проверяют, что функции работают одинаково. Резюмируя — не бойтесь копирования кода, там где это оправдано.

В тему недавно переведённой статьи. Некоторые жалуются на пакет syscall в Go — нужно его изменить, он не портируется. Но syscall-ы это по определению платформозависимые вызовы, поэтому если вы хотите делать качественный кроссплатформенный код — всегда добавляйте build-теги в файлы, в которых используются syscall.

Хороший постулат в тему предыдущей статье (), о том, когда использовать мьютексы, а когда каналы. Мьютексы просто сериализируют исполнение доступа к чему-либо, но горутины и каналы дают вам инструмент создавать программы со сложной архитектурой и структурой обмена данными между частями программы.

Тут делается акцент на том, что код написанный с использованием пакета также не портируемый, и на него не распространяются гарантии обратной совместимости в пределах Go 1 (это написано в первой же строке документации к пакету). Пока что в нём ничего не меняли, но если вы собираетесь использовать зачем-то unsafe — будьте готовы, что в будущих версиях, придётся подстраиваться под изменения.

(способность программы менять собственную структуру) в Go реализована пакетом, и люди нередко пытаются использовать её не к месту. Рефлексия — это очень-очень мощная вещь, но очень сложная для понимания, и на самом деле нужна очень малому количеству людей и задач. Пайк говорит, что написал, вероятно, больше всех кода с рефлексией в Go, и до сих пор ненавидит это делать. И считает, что нужно стимулировать людей не использовать рефлексию, поскольку она, как правило, не нужна так, как им это кажется.

Ещё один важный постулат, который уже неоднократно на хабре, и которому, в частности, в официальном блоге Go. Новички часто не понимают этого и спрашивают — «почему я должен везде писать эти if err != nil». Да потому что вы не программируете, вы клепаете код. Многие считают, что if err != nil — это такая альтернатива try..catch… — специальная конструкция языка для работы с ошибками. Но это не так, вы не можете программировать с try… catch… — это управляющая конструкция. С ошибками же в Go можно и нужно делать то, что требует логика программы.

Многие языки программирования также используют концепт nil. Среди его других названий — NULL, null или None. В 2009 году перед релизом Go проектировщик языков программирования Тони Хоар выступил с презентацией под названием ««. В своей речи Хоар утверждал, что он ответственен за изобретение отсылки null в 1965 году. Он также говорил о том, что указатели в никуда были не лучшей идеей.

В Go nil является более дружелюбен и менее распространен, нежели в других языках программирования, однако и здесь нужно быть готовым к некоторым проблемам. Nil можно использовать не только по его прямому назначению, о чем говорит Франчес Кампой в своей на GopherCon 2016.

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

Избежать сбоя несложно. Это вопрос защиты от разыменования указателя nil с , что показано в следующем листинге.Листинг 2Govar nowhere *int if nowhere != nil { fmt.Println(*nowhere) }

регулярно получают указатели на , что значит приемник может быть nil, как показано в примере ниже. Происходит ли разыменование указателя явно (*p) или неявно через получение доступа к полю структуры (p.age), значение nil вызовет сбой.Листинг 3Gotype person struct { age int } func (p *person) birthday() { p.age++ // разыменование указателя nil } func main() { var nobody *person fmt.Println(nobody) // Выводит: <nil> nobody.birthday() }

Стандартная библиотека
Hugo
статической типизацией
утиная типизация
1974 году
Лисков
англ.
[3]
нетипобезопасны
рефакторинг
докладах
постах
выступление Пайка с одноименным названием
bytes.Buffer
sync.Mytex
rustfmt
выступлении
«Наследие Go»
«Дупликация дешевле плохой абстракции».
IsPrint
«Как писать Go код, который легко портируется»
«Танцы с мьютексами в Go»
unsafe
Рефлексия
reflect
освещался
посвящена статья
Null References: The Billion Dollar Mistake
презентации
разыменованием указателя
оператором if
Методы
структуры
Что такое nil в Golang - NULL, null или None - Примеры кодаGolang
Logo