go-with-me
1.05K subscribers
61 photos
75 links
go-with-me — уютный канал, где Golang становится дружелюбным

Мы — Go-инженеры в BigTech, обожаем делиться своей экспертизой,
пишем статьи о скрытых ловушках, объясняя все на пальцах

— Проводим Go-моки

questions @lovelygopher | @prettygopher
Download Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Какой фон поставить?
Anonymous Poll
62%
Кастомный
38%
Оставить как есть
🔥 GolangConf X — главная встреча Go-сообщества России уже 4 июня в Москве!

📍 Место действия — Loft Hall #4
В программе — хардкорные доклады, живое общение и нетворкинг с топовыми разработчиками

🎤 Доклады, которые точно нельзя пропустить:
Даниил Подольский (YADRO) - Почему вы должны научиться использовать дженерики
Владимир Балун (Независимый эксперт) - Черная магия unsafe в Go
Никита Томчик (Т-Банк) - Когда возможностей Kubernetes недостаточно. Делаем свой DNS-сервер для Kubernetes в связке с CoreDNS
Игорь Панасюк (Яндекс) - Особенности и ловушки модели памяти в Go: тайны синхронизации
Кирилл Кузин (Ви.Tech) - Швейцария в картах Go: путешествие по Swiss Tables
Михаил Люц (Ozon Банк) - Garbage Collector: от появления до современных алгоритмов

🌐 Формат — офлайн и онлайн
Есть всё, чтобы погрузиться в Go, как следует: трансляции, записи, тусовка, сюрпризы от партнёров.

🖥 Резюме
Я всегда мечтал попасть на GolangConf ввиду того, что это отличная возможность прокачать свои харды, понетворкаться с коллегами из разных компаний, получить тонну эмоций и просто кайфануть от вайба. Настоятельно рекомендую к посещению!

🔗 Все детали и регистрация
golangconf.ru/2025

До встречи на GolangConf — будет мощно!

Лично буду присутствовать❗️
Please open Telegram to view this post
VIEW IN TELEGRAM
🌰🌰 Slices. Part 1. Slice creation methods

Зачем их вообще создавать и делать это правильно? Тыкнули make и просто кайфуем, нет так ли? - по моему мнению вопрос достаточно неоднозначен и более глубок

Сегодня мы с вами прокатимся по этому полю и все узнаем

Начнем с попсовой внутрянки слайса. Под капотом это вот такая незамысловатая структура:
type slice struct {
data unsafe.Pointer // Нижележащий массив
len int // Длина (то, сколько элементов мы видим у body)
cap int // Емкость (то, сколько элементов мы можем поместить, чтобы не произвести реаллокации body)
}

Исходники: тык

Это мы добавили для общего развития!

Имеем достаточно громадную тележку опций:
1. var s []int - пустое объявление
— Когда? - не хотим выделять память на внутреннюю структуру slice
— Почему? - При инициализации через var переменная аллокацируется с zero value (nil). Это относится ко всем ссылочным переменным (map, chan, slice, func, interface и any type pointer)
— Кейс: очень частое создание горутин, в которых происходит много объявлений тех или иных слайсов с отложенным заполнением. Это избавляет нас от лишних аллокаций, мы в плюсе!

2. s := []int{} - инициализация с аллокацией внутренней структуры slice
— Когда? - не знаем capacity, не хотим ниловый слайс, но нужно объявить элегантно
— Заметка: слайс с len = 0 и cap = 0 является zerobase объектом

3. s := arr[1:333] - инициализация из массива
— Когда? - необходимо читать / менять элементы существующего массива

4. s := make([]int, 3, 5)
— Заметка: len - количество проинициализированных видимых zero value элементов в слайсе, cap - емкость

5. s := *new([]int)
— Заметка: new инициализирует с аллокацией внутренней структуры, но возвращает указатель

6. s := append([]int(nil), oldSlice…)
— Когда? - не знаем точный размер слайса (либо специально не хотим фиксировать размер), но нужно создать новый, где append вызовет growslice и создаст слайс с размером до ближайшего size class
— Кейс: объекты bytes.Buffer или же strings.Builder используют данный способ для расширения слайса, но пусть для вас это останется для вас непокоримой тайной (ихиххихи)
Почитать: тык

Почитать про виды инициализации переменных: тык

Статью писали с Дашей: @dariasroom

Вот как-то так и никак иначе. На сегодня все!

Stay tuned!
Please open Telegram to view this post
VIEW IN TELEGRAM
💥 Готовящийся пост - бомба, честно говоря

Если кому-то не терпится, есть Early Access. Для этого достаточно забустить наш канал, мы будем очень вам благодарны❗️

BOOST BOOST BOOST

Оставайтесь на связи 🐶
Please open Telegram to view this post
VIEW IN TELEGRAM
✏️ Mock-собеседования. Отзывы

Спасибо, что обратил(ся/ась) к нам, оставь свое честное мнение

Если вам нужно мок-собеседование, смело пишите нам @lovelygopher | @prettygopher | channel direct
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from myStack
if err != nil остаётся

Команда Go решила не менять синтаксис обработки ошибок и закрывает все предложения по упрощению error handling - ни один вариант не получил широкой поддержки ни в команде, ни в сообществе.

Причины:
- Нет консенсуса, нужен ли такой синтаксический сахар
- Привычный способ error handling признан рабочим и идиоматичным
- Изменения усложнят язык и приведут к массовым изменениям в коде
🌰🌰 Slices. Part 2. Slice Escaping. Fast slice allocations

Доставайте тетрадки и ручки ✏️🗒

Тыкались мы в make, да не дотыкались. Сегодня будем дотыкиваться и оптимизировать аллокацию наших байтиков так, чтобы она происходила максимально эффективно

Перед прочтением данного поста вам стоит углубиться в тему Escape Analysis тык

Кратко, как работает Escape Analysis. Значение уходит в секцию Heap, когда:
— Из функции возвращается указатель на эту переменную, находящуюся в рамках scope’a этой функции
— Прокидываем аргумент в interface параметр функции, который может быть оценен компилятором как изменяемое значение (к примеру fmt.Println(v … any))
— Размер объекта превышает его максимальный в соответствии с его size class
— Прокидываем в канал указатели
— Используем переменную в замыкании (обращаемся к переменной outer функции в inner функции)

На берегу обсудим пару моментов:
Slice header может лежать как на стэке, так и на хипе. Все зависит от того, эскейпился он или нет, при этом нижележащий массив также может находиться в одном из этих пространств памяти
— Максимальный размер нижележащего массива слайса, когда он может лежать на стэке - 64 KB. Если превысить данный порог, моментально уйдем на хип

Чтобы узнать, эксейпятся ваши переменные или нет, используйте:
go build -gcflags="-m" main.go

Учтите, что чем больше вы укажете флагов `-m`, тем более развернутый ответ получите


Потоптались на месте, приступим к раскопке гробниц фараонов (примерам):
func ExampleA() []byte {
const size = 64 * 1 << 10 // 64 KB

return make([]byte, 0, size) // Не улетит в хип т.к. капасити известна на момент компиляции
}

func ExampleB(size int) []byte {
return make([]byte, 0, size) // Улетит в хип, т.к это динамически вычисляемый параметр в рантайме
}


С эскейпом разобрались. Перейдем к оптимизациям:

1. Нам нужна быстрая аллокация слайса на стэке, но мы хотим это сделать в обход ограничения в 64 KB. Используем sparse literal оптимизацию: явно определяем значение для индекса maxSize - indexPad с занулением всех элементов до него, а сам слайс обрезаем до [:0]. Получаем len = 0, cap = maxSize
func ExampleC() []byte {
const (
maxSize = 1 << 30
indexPad = 1
)

return []byte{maxSize - indexPad: 0}[:0] // 1 GB попадет на стэк несмотря на ограничение в 64 KB. Это фича, которую я отрыл в исходниках (работает с версии Go 1.17)
}


2. Нам нужно инициализировать слайс быстрее make:
— Наращиваем буфер до нужного размера через strings.Builder (который под капотом динамически расширяет []buf до нужного size class)
— Добираемся до первого байта байт-последовательности нашей строки с помощью unsafe.StringData(...)
— Передаем данный указатель в unsafe.Slice, который вернет нам наш созданный слайс
— Сбрасываем len до нуля с помощью реслайсинга по самому же себе (то есть [:0])
— Заметка: Для работы с unsafe желательно понимать нюансы его использования и иметь высокую экспертизу тык
Этот метод позволит кратно увеличить перформанс!
func ExampleD(size int) []byte {
if size <= 0 {
return nil
}

var b strings.Builder
b.Grow(size)

return unsafe.Slice(unsafe.StringData(b.String()), len(b.String()))[:0] // Работает быстрее дефолтного make
}


Проверим, насколько наши способы эффективнее относительно создания с помощью make
Имеем вот такие бенчмарки:
// goos: darwin
// goarch: arm64
// pkg: workspace
// cpu: Apple M3 Pro
// BenchmarkExampleB-12 100 42463998 ns/op 1073742102 B/op 1 allocs/op
// BenchmarkExampleC-12 100 39844979 ns/op 1073741835 B/op 1 allocs/op
// BenchmarkExampleD-12 100 26116605 ns/op 1073741884 B/op 1 allocs/op

Важно отметить, что для всех бенчмарков использовалась размерность 1<<30 bytes (1 GB)

Выводы:
— Создание через unsafe ExampleD в сравнении с make ExampleA быстрее на 38.50%
— Создание через sparse инициализацию ExampleС в сравнении с ExampleA быстрее на 6.17%

Ваши готовые конспекты и вопросы ждем в комментах 📕

Статью писали с Дашей: @dariasroom

Stay tuned!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤️‍🔥 GolangConf X Как это было?

Это мой первый митап в жизни и я просто в восторге и преисполнен эмоциями. Прошло все феерично и тепло

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

Организаторам огромная благодарность, вы умнички 💗
@onokonem
@Filiko_floro

Я обязательно буду присутствовать на следующей конференции в качестве спикера. Думаю, что настало мое время зажечь!

🔍 Следите за обновлениями и обязательно приходите!
golangconf.ru/2025

В качестве знакомства с вами заливаю чуть-чуть себя, живого и радостного 💗

Stay tuned!
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
💣 Последний пост про слайсы не просто бомба, а большущий крипер из майнкрафта

Небольшая затравка. Нет-нет, мы не будем вас травить, а просто оставим ссылку на код: тык

Будет ли здесь датарейс, если да, то почему? Пишите в комментах

Если вам не терпится, можете получить Early Access. Для этого достаточно просто забустить наш канал ❗️

BOOST BOOST BOOST

Stay tuned 😮
Please open Telegram to view this post
VIEW IN TELEGRAM
🌰🌰 Slices. Part 3. Slice subtleties

В наших планах не было настолько душнить по слайсам, но это вынужденная мера. Я надеюсь, вы еще не устали от них. Да и даже если устали - терпите!

Кратко по содержанию: тут будут фичи, которые частенько спрашивают на собесах

Разберем приемы, которые помогут использовать слайсы очень изящно

1. Slice Length Zeroing
Занулив длину слайса конструкцией: s[:0], мы можем впоследствии зааппендить только нужные нам элементы без лишних аллокаций, так как работаем с тем же нижележащим массивом

— Когда? - фильтрация по определенному предикату

— Пример: исключаем пустые строки из слайса s
func Filter(s []string) []string {
if len(s) == 0 {
return s
}

filtered := s[:0]

for i := range s {
if s[i] == "" {
continue
}

filtered = append(filtered, s[i])
}

return filtered
}


2. Len raising
— Когда? - у вас не должно возникать этого вопроса, просто используйте и кайфуйте

— Пример:
func RaiseLen(s []int, n int) []int {
if n <= 0 || len(s)+n > cap(s) {
return s
}

return s[:len(s)+n]
}


3. Append and Array

Помним, что массив - это отдельная структура данных, а append(dest, src) принимает в себя только слайсы и прямые перечисления элементов

— Когда? - скорее всего вас это спросят как-нибудь на собесе, вы поплывете и пойдете плакать в угол, так что просто держите это в голове

— Заметка: чтобы сделать аппенд массива в слайс вам нужно сделать реслайсинг с помощью конструкции arr[:]

— Примеры (правильный и нет):
func AppendArrayWrong(s []int, arr [1 << 8]int) []int {
return append(s, arr...) // cannot use arr as int value in argument
}

func AppendArrayRight(s []int, arr [1 << 8]int) []int {
return append(s, arr[:]...)
}


4.
FSE (Full Slice Expression)
Наверняка вы видели такое выражение s[0:5:7], где 1 и 2 элемент означают границы слайса, а последний (7) указывает на тот порог емкости, перейдя который, произойдет отвязка от слайса, по которому мы реслайсимся

Поздравляю, вы грокнули FSE. Занавес!

— Когда? - хотим наложить констрейнт на количество добавляемых элементов в реслайснутый слайс

— Пример:
func FullSliceExpressionUsage() {
s := make([]int, 5, 10)

boundedS := append(s[0:5:7], []int{1, 2, 3}...)

s[0] = -1

fmt.Printf("1st elem of \"s\": %d\n", s[0])
fmt.Printf("1st elem of \"s\": %d\n", boundedS[0])
}


5. Slice thread safety

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

Первый пример достаточно большой, поэтому мы засунули его в плейграунд тык. Чтобы проверить, будет ли датарейс в этом случае, запустите код с флагом -race, тобишь:
go run -race main.go


— Примеры:
Правильный: Worker Pool, где каждый из воркеров меняет элемент слайса по отдельному индексу (тык). Вас может смутить семантика sync.RWMutex, но не пугайтесь, он нужен для разделения контекста операций с реверсом логики sync.RWMutex. То есть воркеры конкурентно пишут в слайс используя RLock() с целью сохранить толк от concurrent записей, а отдельный поток чтения выполняет fmt.Println() под Lock() для фиксации снимка слайса

Неправильный: data race из за изменения элемента по одному и тому же индексу
// WARNING: DATA RACE
// ...
func SliceDataRace() {
var wg sync.WaitGroup

s := make([]int, 4, 8)

wg.Add(2)
go func() {
defer wg.Done()

s[3] = -1
}()

go func() {
defer wg.Done()

fmt.Println(s[3])
}()

wg.Wait()
}


Со слайсами мы закончили, слава богу! Теперь вы готовы разрубать их как самураи

Будем двигаться дальше. Куда? - не скажем

Статью писали с Дашей: @dariasroom

💭 В качестве личной рекомендации советую курс "Глубокий Go" Владимира Балуна. Там вы сможете получить исчерпывающую информацию по гошечке

➡️ Подробнее тык

🛍 Для вас мы подготовили промокод на скидку в 5%GO_WITH_ME

Stay tuned 🙃
Please open Telegram to view this post
VIEW IN TELEGRAM
Друзья, новый материал уже не за горами

Готовим много новых статей. Приготовьтесь, мы взлетаем.

По известной схеме: если вам не терпится, можете получить Early Access. Для этого достаточно просто забустить наш канал. Помогите нам сохранить уют, это очень важно

❤️ Кто бустил ранее и хочет заполучить ранний доступ, отпишите в личку @lovelygopher

BOOST BOOST BOOST

Stay tuned 🐐
Please open Telegram to view this post
VIEW IN TELEGRAM
Рекомендуем отличный канал моих коллег — Ани, Кирилла и Эдгара из команды gIT

gIT - это Go To IT, канал, знакомящий с IT под новыми ракурсами и рассказывающий об интересных проектах и технологиях

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

🔗 У них есть и телеграм канал, и YouTube && RuTube

Так что велком!
Please open Telegram to view this post
VIEW IN TELEGRAM
🍀 Tricky defers. Part 1. Key features

Бьемся челом и на коленях просим прощения за целых 3 лонгрида по слайсам и даем вам глотнуть свежего воздуха

По нашему опыту при работе с defer'ами многие упускают тонкости, описанные в данной статье

Пристегните ваши ремни, откройте рты пошире. Дышать будем медленно и глубоко!


1. Передача параметров в defer функцию

1.1 Прямая передача аргументов
Здесь используется тот же механизм, что и в обычных функциях: переданные аргументы копируются внутрь стека функции defer on the fly (фиксируем стейты передаваемых аргументов в момент вызова defer)
— Пример
// the world
// the sun is shining
// tasty bebra
func DirectPassing() {
s := "bebra"

defer func(s string) {
fmt.Println("tasty", s)
}(s)

s = "the sun"

defer func(s string) {
fmt.Println(s, "is shining")
}(s)

s = "the world"
fmt.Println(s)
}


1.2 Захват переменных замыканием
При использовании внутри defer переменных из outer функции получаем ситуацию равносильную передаче аргументов по указателю, следовательно в defer вызове получим конечные стейты переменных
— Пример
// the world
// the world is shining
// tasty the world
func ClosurePassing() {
s := "bebra"

defer func() {
fmt.Println("tasty", s)
}()

s = "the sun"

defer func() {
fmt.Println(s, "is shining")
}()

s = "the world"
fmt.Println(s)
}



2. Именованные и неименованные возвратные значения
Если данная часть будет вам непонятна, можете почитать про момент вызова defer в Go Specification

2.1 Неименованные возвратные значения
Если мы используем привычную конструкция func A() int, а после попытаемся изменить переменную int, которая была объявлена в рамках scope’а функции, в defer, изменения не затронут возвратное значение функции A(), потому что int копируется в return-регистр ДО вызова defer
— Пример
// num value before function exiting: 200
// num value in the 2nd defer `before`: 200
// num value in the 2nd defer `after`: 300
// num value in the 1st defer `before`: 100
// num value in the 1st after `after`: 777
// Func result: 200
func UnnamedReturn() int {
num := 100

defer func(num int) {
fmt.Printf("num value in the 1st defer `before`: %d\n", num)
num = 777
fmt.Printf("num value in the 1st after `after`: %d\n", num)
}(num)

num = 200

defer func() {
fmt.Printf("num value in the 2nd defer `before`: %d\n", num)
num = 300
fmt.Printf("num value in the 2nd defer `after`: %d\n", num)
}()

fmt.Printf("num value before function exiting: %d\n", num)
return num
}


2.2 Именованные возвратные значения
Когда мы определяем возвратные значения как именуемые в сигнатуре функции, эти аргументы живут ровно до выхода из функции, а значит наши return регистры будут содержать актуальные данные
— Заметка: в примере ниже используется naked return. Идиоматичным подходом является не мешать явные и неявные возвраты, имейте это в виду
— Пример
// Defer with closure close phuong-secrets.txt: file already closed
// Func result: close phuong-secrets.txt: file already closed
func NamedReturn() (err error) {
const (
filePath = "phuong-secrets.txt"
)

f, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("opening the file %q: %w", filePath, err)
}
defer func() {
err = errors.Join(err, f.Close())
fmt.Println("Defer with closure", err)
}()

sr := bufio.NewScanner(f)
for sr.Scan() {
_ = sr.Text()
}

// It's made deliberately to show the changes in defer statement
f.Close()

return
}



Надеюсь, вы очень хорошо надышались и полны сил. Отпускаем вас на заслуженный отдых!

Статью писали с Дашей: @dariasroom

Stay tuned 🌷
Please open Telegram to view this post
VIEW IN TELEGRAM