Библиотека Go для собеса | вопросы с собеседований
6.87K subscribers
218 photos
6 videos
1 file
417 links
Вопросы с собеседований по Go и ответы на них.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/0b524a15

Для обратной связи: @proglibrary_feeedback_bot

Наши каналы: https://t.me/proglibrary/9197
Download Telegram
💬 Как использовать string canonicalization для экономии памяти?

В Go, строки неизменяемы и часто одинаковые строки могут иметь разные блоки памяти. String canonicalization позволяет этим строкам делить один и тот же блок памяти, что может значительно сэкономить память. Рассмотрим два способа реализации на Go.

1️⃣ Первый способ предполагает, что когда две строки равны, одна строка может заменить другую, чтобы они делили одну и ту же память.


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
}
}


2️⃣ Использование unique.Handle, представленного в Go 1.23.

Этот способ более удобен и гибок:


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)
}
}
👍61
💬 Как итерироваться по рунам в строке Go?

Для получения рун в строке достаточно использовать конструкцию for-range.

for i, rune := range aString {
... // используем руну
}


Руна может состоять из 1-4 байтов в кодировке UTF-8. Здесь i является начальным индексом байтов руны. Если нам нужно не только получить сами руны, но и их длину, удобным способом будет использование пакета unicode/utf8:

import "unicode/utf8"

var str = aString
for {
rune, size := utf8.DecodeRuneInString(str)
if size == 0 {
break
}
str = str[size:]

... // используем руну
}
💵⚡️ДАРИМ 40 000₽ ЗА ВИДЕО

Конкурс года в «Библиотеке программиста»: смонтируйте короткий вертикальный ролик формата Shorts/Reels* на тему программирования и разработки — лучший автор получит 40 тысяч рублей 🤑

Подробные условия:
➡️смонтируйте короткий смешной вертикальный ролик (можно и нужно использовать мемы)
➡️отправьте нам в бота @ProglibContest_bot
➡️лучшие ролики (по мнению редакции) мы будем выкладывать в канал и в наш инстаграм*
➡️тот, чей ролик соберет больше всего просмотров в инстаграм*, получит приз — 40 тысяч рублей

Какие ролики мы не принимаем:
😟не вашего авторства (проверим!)
😟длинные, невертикальные, несмешные

Таймлайн:
2 августа — заканчиваем принимать видео
⬇️
3 августа — начинаем загружать лучшие видео в инстаграм
⬇️
9 августа — подводим итоги

*Организация Meta признана экстремистской в РФ
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
✍️ «Библиотека программиста» находится в поиске автора на написание книжных рецензий

Кто нужен?
● Энтузиасты (джуны и выше), которые которые разбираются в IT
● Любители книг, которые хотели бы получать деньги за чтение и написание рецензий
● Работаем с самозанятыми (компенсируем налог), ИП

Мы предлагаем частичную занятость и полностью удаленный формат работы — можно совмещать с основной и находиться в любом месте🌴

✉️ Станьте частью нашей команды — присылайте резюме и примеры работ hello@proglib.io
💬 Как получить адреса байтов строки в Go?

1. Использование unsafe до версии Go 1.22. До версии Go 1.22 получение адресов байтов строки было возможно только через пакет unsafe. Это небезопасный способ, но он работает:


package main

import (
"unsafe"
)

func main() {
str := "Hello, Go!"
addr := unsafe.StringData(str)
println(addr, string(*addr))
}


2. Использование reflect с версии Go 1.23

Начиная с версии Go 1.23, метод reflect.Value.UnsafePointer позволяет получить адреса байтов строки:


package main

import (
"reflect"
)

func main() {
str := "Hello, Go!"
addr := (*byte)(reflect.ValueOf(str).UnsafePointer())
println(addr, string(*addr))
}


3. Конвертация строки в срез байтов. С версии Go 1.22 была добавлена оптимизация, которая позволяет конвертировать строки в срезы байтов без копирования памяти, если байты не будут изменяться. Это позволяет безопасно получать адреса байтов строки:


package main

func main() {
str := "Hello, Go!"
byteSlice := []byte(str)
addr := &byteSlice[0]
println(addr, string(*addr))
}
🔥5👍1
💬 Перечислите все способы клонирования срезов на Go?

1️⃣ Использование make и copy


cloned := make(SliceType, len(aSlice))
copy(cloned, aSlice)


2️⃣ Более развернутая форма 1 способа


var cloned SliceType
if aSlice != nil {
cloned = make(SliceType, len(aSlice))
copy(cloned, aSlice)
}


3️⃣ Использование make и append


append(make(SliceType, 0, len(aSlice)), aSlice...)


4️⃣ Использование append


append(SliceType(nil), aSlice...) // Вариант 1
// или
append(SliceType{}, aSlice...) // Вариант 2


5️⃣ Другой способ использования append


append(aSlice[:0:0], aSlice...)


6️⃣ Использование slices.Clone (Go 1.21+)


import "slices"
slices.Clone(aSlice)
👍17🥱10
🧑‍💻 Статьи для IT: как объяснять и распространять значимые идеи

Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.

Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.

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

👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
💬 Назовите основные способы создания среза в Go.

Предположим, что тип нового создаваемого среза — это S, а s0 — это нулевое значение литерала типа S.

1. Использование составных литералов:
// или 
var aSlice = S{Len - 1: s0} // длина и емкость равны Len (константа)


Емкость результирующего среза не всегда выравнивается по размеру класса памяти.

2. Использование встроенной функции make:

// или
var aSlice = make(S, initialLength) // емкость == initialLength


Емкость результирующего среза не всегда выравнивается по размеру класса памяти.

3. Использование встроенных функций append + make):


// или
var aSlice = append(S(nil), make(S, initialLength)...)
// или
var aSlice = append(S{}, make(S, initialLength, capacity)...)
// или
var aSlice = append(S(nil), make(S, initialLength, capacity)...)


С официальным Go-инструментарием емкость результирующего среза выравнивается по размеру класса памяти.
👍5
💬 Как создать байтовый срез на Go, не обнуляя его байтовые элементы?

До версии Go 1.21 этого добиться было невозможно, даже небезопасными способами. Начиная с Go 1.21, реализация strings.Builder.Grow вызывает внутреннюю функцию bytealg.MakeNoZero вместо встроенной функции make, которую вызывала старая реализация.

В большинстве случаев встроенная функция make обнуляет элементы результирующего среза, поэтому она часто сравнительно медленнее.
С реализацией версии 1.21+ у нас появилась возможность создавать байтовые срезы без инициализации их элементов нулем (хотя для достижения этой функциональности требуется использование функций unsafe)


import (
"strings"
"unsafe"
)

func MakeDirtyByteSlice(n int) []byte {
var b strings.Builder
b.Grow(n)
var p = unsafe.StringData(b.String())
return unsafe.Slice(p, n)
}
👍93
💬 Назовите основные функции и механизмы, которые могут остановить или приостановить выполнение текущей горутины в Go.

1. time.Sleep(d time.Duration): приостанавливает горутину на указанный период времени.
2. runtime.Gosched(): уступает выполнение другим горутинам, временно приостанавливая текущую.
3. runtime.Goexit(): немедленно завершает текущую горутину, выполняя все отложенные вызовы.
4. Блокировка в канале: горутина приостанавливается, ожидая отправки или получения данных через канал.
5. sync.Mutex: горутина блокируется при вызове Lock(), ожидая освобождения мьютекса.
6. context.Context: приостанавливает горутину, слушающую канал Done(), когда контекст отменяется.
👍16🔥2
🍇 18 основных паттернов микросервисной архитектуры

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

Читать статью
4🥱2
💬 Какие типы оператора defer существуют в Go?

📌 До Go 1.13: heap-allocated defer

До версии Go 1.13 все объекты defer выделялись в куче. Это могло приводить к снижению производительности из-за затрат на выделение памяти в куче.

📌 Go 1.13: stack-allocated defer

С версии Go 1.13 была введена возможность выделения объектов defer в стеке. Это позволило улучшить производительность за счет уменьшения затрат на выделение памяти.

📌 Go 1.13+: open-coded defer

Также с версии Go 1.13 была введена оптимизация для встраивания defer в конец функции и перед каждым оператором return. Это значительно улучшило производительность, но применимо только в ограниченных кейсах, например, когда количество defer-операторов невелико (не более 8) и нет динамических конструкций, таких как циклы.

📌 Go 1.22: heap-allocated defer в циклах

В Go 1.22, если defer используется внутри цикла, он по-прежнему выделяется в куче, поскольку количество объектов defer может динамически меняться во время выполнения.

👉 Подробнее
👍12
🌐 9 основных паттернов для проектирования распределенных систем

В этой статье мы рассмотрим 9 основных паттернов и области их применения, что поможет вам в проектировании высоконагруженных приложений.

Читать статью
❤‍🔥2
💬 Как эффективно удалять смежные элементы среза, сохраняя порядок элементов?

Предположим:
1. Задействованный срез обозначен как s, и его тип — []E, где E — тип элементов среза.
2. Мы хотим удалить элементы в s[from:to], где from <= to.

📌 Способы удаления элементов:

1️⃣
s = s[:from + copy(s[from:], s[to:])]


2️⃣
s = append(s[:from], s[to:]...)


3️⃣ С использованием пакета slices:
import "slices"
s = slices.Delete(s, from, to)


Если используется Go toolchain 1.21, то после вызова функции slices.Delete мы должны самостоятельно очистить освобожденные элементы. Начиная с Go 1.22, функция slices.Delete автоматически очищает освобожденные элементы.
👍11
🐘🔮 Иллюзия согласованности в PostgreSQL: как избежать неприятных сюрпризов

Несмотря на то, что PostgreSQL обеспечивает строгую согласованность данных благодаря использованию многоверсионного параллельного контроля (MVCC), порядок выполнения транзакций может быть неожиданным для клиента. Это может привести к ситуации, когда записи появляются в неправильном порядке. Рассказываем, как и почему это происходит, и что делать для минимизации подобных проблем.

Читать статью
👍3
💬 Какими свойствами должны обладать ключи в map'е?

Ключи должны поддерживать операции сравнения == и != (comparable). Это необходимо, чтобы Go мог эффективно выполнять операции поиска, вставки и удаления элементов из мапы.
👍8
💬 В каких режимах может работать мьютекс в Go?

Мьютекс может работать в одном из двух режимов: нормальный и starvation.

В нормальном режиме горутины, ожидающие мьютекса, организованы в очередь по принципу FIFO. Однако, когда горутина пробуждается и пытается захватить мьютекс, она не получает управление сразу. Вместо этого она вынуждена конкурировать с новыми горутинами, которые также хотят захватить мьютекс в этот момент.

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

📌 Пример сценария в нормальном режиме: допустим, горутина, которая только что проснулась, часто проигрывает эту гонку новым претендентам и снова попадает в начало очереди. Если горутине не везет, она может каждый раз просыпаться именно тогда, когда приходит новая горутина, что приводит к тому, что она никогда не захватывает мьютекс. Именно для таких случаев предусмотрен переход мьютекса в режим starvation.

Режим starvation включается, если горутина не может захватить мьютекс более 1 миллисекунды. Этот режим гарантирует, что ожидающие горутины получат справедливый шанс на захват мьютекса.

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

📌 Пример сценария в режиме starvation: мьютекс последовательно предоставляет доступ горутинам G1, G2 и так далее. Каждая ожидающая горутина получает управление и проверяет два условия: является ли она последней горутиной в очереди; ожидала ли она менее одной миллисекунды.

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

Такое поведение мьютекса в Go помогает сбалансировать конкурентный доступ к общим ресурсам, обеспечивая справедливость и предотвращая бесконечное ожидание горутин в очереди.

👉 Подробнее
🔥25👍7💯1
💬 Какой основной юзкейс функции TryLock?

В Go 1.18 была введена новая функция TryLock для мьютексов (sync.Mutex и sync.RWMutex), которая позволяет попытаться захватить блокировку в неблокирующем режиме. Это означает, что если блокировка уже занята, функция просто вернет значение false, вместо того чтобы ждать освобождения блокировки.

Она может быть полезна в ситуациях, когда требуется неблокирующая попытка захватить ресурс. Например, если очередь может быть очищена любой горутиной и не нужно очищать ее несколько раз подряд, первая горутина, которая захватит блокировку, выполнит эту задачу, а остальные могут продолжить работу.
👍10