Вы хотите успешно сдать собеседование по языку программирования Go? Тогда канал "Библиотека Go для собеса | вопросы с собеседований" (@go_interview_lib) станет вашим надежным помощником! Здесь вы найдете самые актуальные вопросы и их ответы, которые помогут вам подготовиться к собеседованию на высоком уровне.
Не забудьте также ознакомиться с другими нашими каналами: https://t.me/proglibrary/9197
Посетите наш веб-сайт для обучения: https://proglib.io/w/0b524a15
Если у вас есть вопросы или предложения, вы всегда можете обратиться к нашему боту обратной связи: @proglibrary_feedback_bot.
Для информации о рекламе на наших каналах обращайтесь к @proglib_adv. Подробный прайс можно узнать у @proglib_advertising. Не упустите возможности улучшить свои навыки и успешно пройти собеседование благодаря каналу "Библиотека Go для собеса | вопросы с собеседований"!
23 Jan, 13:02
22 Jan, 15:04
22 Jan, 07:04
21 Jan, 07:03
20 Jan, 11:07
20 Jan, 07:06
19 Jan, 07:05
18 Jan, 07:02
17 Jan, 13:00
16 Jan, 07:06
15 Jan, 15:00
15 Jan, 06:02
14 Jan, 15:00
13 Jan, 20:02
12 Jan, 20:59
12 Jan, 19:00
11 Jan, 18:36
11 Jan, 18:22
08 Jan, 13:02
06 Jan, 13:06
06 Jan, 13:00
05 Jan, 10:59
04 Jan, 13:01
03 Jan, 15:20
03 Jan, 10:59
02 Jan, 12:59
01 Jan, 13:00
31 Dec, 13:00
30 Dec, 20:03
30 Dec, 13:00
30 Dec, 10:45
30 Dec, 08:34
28 Dec, 16:00
27 Dec, 11:01
26 Dec, 11:04
25 Dec, 11:01
24 Dec, 11:02
23 Dec, 18:01
func slicesEqual(a, b []int) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
import "reflect"
func slicesEqual(a, b []int) bool {
return reflect.DeepEqual(a, b)
}
import (
"fmt"
"github.com/google/go-cmp/cmp"
)
func main() {
slice1 := []int{1, 2, 3}
slice2 := []int{1, 2, 3}
fmt.Println(cmp.Equal(slice1, slice2)) // true
}
import (
"encoding/json"
"log"
)
func slicesEqualJSON(a, b []int) bool {
aJSON, _ := json.Marshal(a)
bJSON, _ := json.Marshal(b)
return string(aJSON) == string(bJSON)
}
23 Dec, 15:01
22 Dec, 11:05
21 Dec, 11:01
20 Dec, 09:01
20 Dec, 07:07
19 Dec, 11:05
18 Dec, 12:59
18 Dec, 12:29
17 Dec, 18:05
17 Dec, 07:43
package main
import "fmt"
func main() {
i := 0
defer fmt.Println("Result:", i)
i++
}
16 Dec, 20:04
16 Dec, 13:00
15 Dec, 13:00
15 Dec, 06:01
14 Dec, 13:01
13 Dec, 13:00
13 Dec, 07:02
04 Dec, 11:02
04 Dec, 07:06
03 Dec, 18:06
02 Dec, 20:03
02 Dec, 18:05
{«level»: «info», «message»: «User logged in», «user_id»: 123 }
01 Dec, 18:08
30 Nov, 11:05
29 Nov, 11:01
28 Nov, 18:06
28 Nov, 13:54
27 Nov, 13:43
27 Nov, 12:12
26 Nov, 18:05
26 Nov, 11:18
25 Nov, 15:21
25 Nov, 07:09
gopackage main
import (
«fmt»
«sync»
)
var counter int
var mu sync.Mutex
func increment(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 1000; i++ {
mu.Lock() // Блокируем доступ
counter++
mu.Unlock() // Освобождаем доступ
}
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go increment(&wg)
go increment(&wg)
wg.Wait()
fmt.Println(«Counter:», counter) // Правильное значение}
gopackage main
import «fmt»
func main() {
ch := make(chan int)
go func() {
sum := 0
for i := 0; i < 1000; i++ {
sum++
}
ch <- sum
}()
result := <-ch
fmt.Println(«Result:», result) // Гарантированно корректное значение}
gopackage main
import ( «fmt» «sync» «sync/atomic»)
var counter int64
func increment(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 1000; i++ {
atomic.AddInt64(&counter, 1) // Атомарное увеличение
}
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go increment(&wg)
go increment(&wg)
wg.Wait()
fmt.Println(«Counter:», counter) // Корректное значение}
go run -race main.go
23 Nov, 20:47
nil
-срез — это валидный срез с нулевой длиной (len == 0
) и нулевой ёмкостью (cap == 0
).append()
на nil
-срезе, Go выделяет новую память и создаёт срез с достаточной ёмкостью для хранения новых элементов. Поэтому добавление элементов в nil
-срез работает без ошибок.var a []int
a = append(a, 4, 5, 6) // Создаётся новый срез в памяти
fmt.Println(a) // [4 5 6]
nil
-мапа неинициализирована и не имеет памяти для хранения пар ключ-значение.nil
-мапу вызовет панику, так как мапа не готова к использованию.make
.var m map[int]int
// m[1] = 1 // Вызовет панику
m = make(map[int]int)
m[1] = 1 // Теперь работает
22 Nov, 20:42
21 Nov, 20:56
20 Nov, 19:39
bytes.Buffer
содержит срез []byte
. Если скопировать объект Buffer
, методы, вызванные на копии, могут изменять данные в оригинале, так как оба объекта используют одну и ту же память.package main
import (
"bytes"
"fmt"
)
func main() {
// Создаем оригинальный буфер
original := bytes.Buffer{}
original.WriteString("Hello")
// Копируем буфер
copyBuffer := original
// Добавляем данные в копию
copyBuffer.WriteString(", World!")
// Проверяем содержимое оригинала
fmt.Println(original.String()) // Вывод: "Hello, World!"
}
[]byte
в копии copyBuffer
ссылается на тот же массив, что и в оригинале original
. Поэтому изменения в одном буфере затрагивают другой. 19 Nov, 18:40
18 Nov, 20:02
17 Nov, 20:43
var t []string
вместо t := []string{}
. Первый вариант объявляет срез как nil, в то время как второй вариант создаёт не-nil срез нулевой длины. Они функционально эквивалентны — их len и cap равны нулю, — но стиль с nil-срезом считается предпочтительным.[]string{}
— как пустой JSON-массив []
. 16 Nov, 20:43
math/rand
, и как правильно сгенерировать безопасный ключ?rand.Seed()
генератор math/rand
всегда возвращает одну и ту же последовательность чисел.time.Now().UnixNano()
как seed, доступно всего несколько бит энтропии, что делает последовательности легко предсказуемыми.math/rand
не подходит для криптографических задач, так как его алгоритм детерминированный и может быть взломан.crypto/rand Reader
— альтернативный вараинт , а если нам нужен текст, можно вывести его в шестнадцатеричном формате или в формате base64
:import (
"crypto/rand"
// "encoding/base64"
// "encoding/hex"
"fmt"
)
func Key() string {
buf := make([]byte, 16)
_, err := rand.Read(buf)
if err != nil {
panic(err) // out of randomness, should never happen
}
return fmt.Sprintf("%x", buf)
// or hex.EncodeToString(buf)
// or base64.StdEncoding.EncodeToString(buf)
}
15 Nov, 19:59
io.WriterTo
оптимизирует передачу данных в Go и как io.Copy()
решает, какой метод использовать?io.WriterTo
позволяет объекту-источнику напрямую записывать данные в объект-получатель через метод WriteTo(w Writer)
. Это устраняет необходимость промежуточного буфера, делая передачу данных более эффективной.io.Copy()
проверяет, реализует ли объект-источник интерфейс WriterTo
или объект-получатель интерфейс ReaderFrom
:WriterTo
, вызывается WriteTo()
.ReaderFrom
, вызывается ReadFrom()
.sendfile
в Linux. 15 Nov, 06:03
12 Nov, 20:35
json.Unmarshal
обрабатывает совпадения ключей в JSON при разном регистре, и какой результат будет при наличии нескольких ключей, отличающихся только регистром?json.Unmarshal
при декодировании JSON-сообщений в структуру Go сначала пытается найти точное совпадение ключа. Если точное совпадение не найдено, она принимает ключи, совпадающие с именем поля структуры без учёта регистра.json.Unmarshal
выберет последнее упомянутое значение с учётом регистра. Например, если в JSON имеются ключи "HTML"
и "html"
, то при декодировании в поле HTML
структуры будет записано значение из последнего из них."HTML"
и "html"
с разными значениями, но функция Unmarshal
записывает значение из последнего ключа ("bar"
) в поле HTML
структуры:package main
import (
"encoding/json"
"fmt"
)
type T struct {
HTML string `json:"HTML"`
}
var s = `{"HTML": "foo", "html": "bar"}`
func main() {
var t T
if err := json.Unmarshal([]byte(s), &t); err != nil {
fmt.Println(err)
return
}
fmt.Println(t.HTML) // bar
}
json.Unmarshal
предпочтёт последнее значение. 11 Nov, 20:13
s != ""
или len(s) == 0
? В чем разница между этими подходами?s != ""
, так как это делает код более читаемым и сразу показывает, что s
— это строка. len(s) == 0
также работает, но это более универсальный способ, который подходит для проверки пустоты слайсов, мап и других типов данных, поэтому может быть менее очевидно, что s
— строка. 10 Nov, 19:32
10 Nov, 14:25
09 Nov, 15:40
runtime.SetFinalizer
, позволяющая привязать финализатор к объекту. Как она работает и в каких случаях использование финализаторов может быть небезопасным? Приведите пример ситуации, в которой финализатор может не сработать.runtime.SetFinalizer
позволяет привязать функцию (финализатор) к объекту, которая будет выполнена, когда сборщик мусора сочтёт объект неиспользуемым. Финализатор запускается сборщиком мусора, но его выполнение зависит от цикла GC, поэтому точное время выполнения не гарантируется.FourBytes
является "tiny object", и финализатор может не сработать, если другие объекты из этой же группы продолжают использоваться:type FourBytes struct {
A byte; B byte; C byte; D byte
}
func final() {
a := &FourBytes{}
runtime.SetFinalizer(a, func(a *FourBytes) {
fmt.Println("Финализатор FourBytes вызван")
})
}
func main() {
final()
runtime.GC()
time.Sleep(time.Millisecond)
}
Close
или Dispose
. 06 Nov, 20:50
if str1 == str2 {
str1 = str2 // освобождаем базовый блок памяти str2
}
func CanonicalizeStrings(ss []string) {
type S struct {
str string
index int
}
var temp = make([]S, len(ss))
for i := range temp {
temp[i] = S {
str: ss[i],
index: i,
}
}
for i := 0; i < len(temp); {
var k = i+1
for j := k; j < len(temp); j++ {
if temp[j].str == temp[i].str {
temp[j].str = temp[i].str
temp[k], temp[j] = temp[j], temp[k]
k++
}
}
i = k
}
for i := range temp {
ss[temp[i].index] = temp[i].str
}
}
unique.Handle
для удобной канонизации строк.import "unique"
func CanonicalizeString(s string) string {
return unique.Make(s).Value()
}
func CanonicalizeStrings(ss []string) {
for i, s := range ss {
ss[i] = CanonicalizeString(s)
}
}
CanonicalizeString
ко всем строкам во время выполнения, и тогда все одинаковые строки будут делить одни и те же базовые блоки памяти.unique.Make
не всегда подходит для всех ситуаций. Функция unique.Make
выделяет блок памяти для каждой уникальной строки. Если некоторые неравные строки, подлежащие канонизации, уже делят один и тот же блок памяти, unique.Make
выделит новый блок памяти для каждой из строк, что может привести к увеличению использования памяти (вместо экономии). 05 Nov, 20:58
sync․Map
под капотом поддерживает две мапы, что может быть неидеально, если вы часто добавляете или удаляете ключи.05 Nov, 09:01
04 Nov, 20:02
02 Nov, 19:40
01 Nov, 20:51
OnceFunc
: функция, которая оборачивает другую функцию так, что та выполнится только один раз. Например, если обёртку wrapper()
вызвать несколько раз, внутренняя функция запустится только при первом вызове. Удобно для случаев, когда необходимо гарантировать, что операция выполняется строго один раз, но хочется иметь удобный многократный интерфейс вызова.OnceValue[T]
: используется для функций, возвращающих одно значение. Это полезно для кеширования результата тяжёлой операции, например загрузки конфигурации, чтобы каждый вызов возвращал тот же результат, экономя ресурсы.OnceValues[T, K]
: это расширение, позволяющее возвращать несколько значений, включая ошибки, что упрощает обработку потенциальных ошибок во время первого вызова функции. Все последующие вызовы вернут закешированный результат (вместе с ошибкой, если она была). 30 Oct, 20:12
bufio.Scanner
— простой и эффективный способ построчного чтения, который автоматически обрабатывает строки и подойдёт для большинства задач.bufio.Reader
с ReadString
предоставляет больше контроля над чтением, позволяя задать разделитель, что может быть полезно для нестандартных форматов.ioutil.ReadFile
используется для чтения всего файла в память, но подходит только для небольших файлов, так как при больших объёмах данных это может перегрузить память. ioutil.ReadFile
устарела. Вместо нее используйте os.ReadFile
. 29 Oct, 20:55
28 Oct, 13:41
26 Oct, 20:01
a := []int{0, 1, 2, 3, 4, 5}
var b[3]int = a[0:3]
// cannot use a[0:3] (value of type []int) as [3]int value in variable
// declaration compiler(IncompatibleAssign)
// Go 1.20
func main() {
a := []int{0, 1, 2, 3, 4, 5}
b := [3]int(a[0:3])
fmt.Println(b) // [0 1 2]
}
// Go 1.17
func main() {
a := []int{0, 1, 2, 3, 4, 5}
b := *(*[3]int)(a[0:3])
fmt.Println(b) // [0 1 2]
}
a[:3]
вместо a[0:3]
. 26 Oct, 06:02
25 Oct, 20:43
io.Seeker
? io.Seeker.
Он позволяет перемещать указатель файла на другую позицию в файле, чтобы можно было начать чтение или запись с нужного места. offset,
который указывает, насколько нужно сместить курсор, и whence,
который задаёт точку отсчёта: 25 Oct, 11:13
22 Oct, 20:45
type JSONResponse = map[string]interface{}
type OrderID = string
type PaymentStatus = string
const (
Pending PaymentStatus = "pending"
Completed PaymentStatus = "completed"
Failed PaymentStatus = "failed"
)
type List[T any] = []T
func printList[T any](list List[T]) {
for _, item := range list {
fmt.Println(item)
}
}
21 Oct, 20:03
21 Oct, 19:54
A
, который имеет метод Hello()
. Когда мы передаём экземпляр A
в функцию Greet()
, приведение успешно выполнится к интерфейсу Greeter
, и будет вызван метод Hello()
.A
, но не хотим, чтобы его метод Hello()
был доступен. Мы можем скрыть его с помощью embedding.Greet(ANoHello{A: A{}})
, приведение типа к интерфейсу не удаётся.ANoHello
встраивает тип A
, который имеет корректный метод Hello()
, приоритет получает неэкспортируемый тип noHello
, который скрывает метод Hello()
. 20 Oct, 20:33
17 Oct, 20:54
package main
import (
"flag"
"fmt"
)
func main() {
name := flag.String("name", "Worl "a name to say hello to")
flag.Parse()
fmt.Printf("Hello, %s!\n", *name)
}
go run main.go -name=Gopher
Hello, Gopher!
16 Oct, 09:01
15 Oct, 20:56
internal
в Go — это специальная структура проекта, которая делает код, находящийся внутри неё, доступным только для внутреннего использования в текущем модуле или его подмодулях. Она служит для того, чтобы скрыть детали реализации и предотвратить случайное или преднамеренное использование этого кода другими модулями или внешними проектами.internal
является «частным» и не предназначен для использования другими модулями.internal
, даже если кто-то захочет это сделать.internal
, не беспокоясь о том, что сломаем зависимости внешних пользователей.internal
, даже если позже это потребуется. Это может вызвать путаницу для новичков, которые не знают этой особенности.15 Oct, 06:03
13 Oct, 16:30
sync.Map
в Go предоставляет набор атомарных методов для работы с мапой, поддерживающих конкурентный доступ: Store().
loaded
будет равно true.
Если ключа нет, он сохраняет новое значение и возвращает его, при этом loaded
будет равно false.
loaded
будет true.
Если ключ не найден, возвращается false.
true,
иначе — false.
f
к каждой паре ключ-значение. Если функция возвращает false,
итерация прекращается (аналогично break
в цикле for
). 12 Oct, 19:00
12 Oct, 06:23
10 Oct, 20:56
09 Oct, 20:50
07 Oct, 20:02
06 Oct, 20:44
struct{}
, который не занимает память:package main
import "fmt"
// Set — коллекция уникальных элементов
type Set struct {
elements map[string]struct{}
}
// NewSet создает новое множество
func NewSet() *Set {
return &Set{elements: make(map[string]struct{})}
}
// Add добавляет элемент
func (s *Set) Add(value string) {
s.elements[value] = struct{}{}
}
// Remove удаляет элемент
func (s *Set) Remove(value string) {
delete(s.elements, value)
}
// Contains проверяет наличие элемента
func (s *Set) Contains(value string) bool {
_, found := s.elements[value]
return found
}
// List возвращает все элементы в виде среза
func (s *Set) List() []string {
keys := make([]string, 0, len(s.elements))
for key := range s.elements {
keys = append(keys, key)
}
return keys
}
// Пример использования множества
func main() {
set := NewSet()
set.Add("apple")
set.Add("banana")
fmt.Println("Contains 'apple':", set.Contains("apple"))
fmt.Println("Set elements:", set.List())
set.Remove("banana")
fmt.Println("Set after removal:", set.List())
}
set1 := NewSet()
set1.Add("apple")
set1.Add("banana")
set2 := NewSet()
set2.Add("banana")
set2.Add("orange")
unionSet := set1.Union(set2)
fmt.Println("Union:", unionSet.List()) // ["apple", "banana", "orange"]
04 Oct, 20:57
03 Oct, 20:58
type Reader interface {
Read(p []byte) (n int, err error)
}
type ReadCloser interface {
Read(p []byte) (n int, err error)
Close() error
}
func CopyData(r Reader, w Writer) error {
buf := make([]byte, 1024)
for {
n, err := r.Read(buf)
if err != nil {
return err
}
if n == 0 {
break
}
if _, err := w.Write(buf[:n]); err != nil {
return err
}
}
return nil
}
func NewBuffer() *bytes.Buffer {
return &bytes.Buffer{}
}
type ReadCloser interface {
Reader
Closer
}
02 Oct, 20:48
time.Timer
и time.Ticker
.time.Timer
, чтобы подождать 2 секунды перед выполнением кода:package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(2 * time.Second)
<-timer.C
fmt.Println("Timer expired!")
}
ticker
, который запускается каждую секунду:package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(1 * time.Second)
for t := range ticker.C {
fmt.Println("Tick at", t)
}
}
01 Oct, 09:42
29 Sep, 20:50
28 Sep, 19:28
26 Sep, 20:46
nil
, и он не указывает на какой-либо выделенный блок памяти. Его длина и ёмкость равны нулю, а сам срез nil
. s := make([]int, 0)
или s := []int{}
). Его длина и ёмкость равны нулю, но сам срез не является nil
.nil
.nil
. 23 Sep, 20:02