Go Level Up
268 subscribers
3 photos
5 links
Go Hard or Go Home

Контакт для связи со мной: @sQUARyss
Download Telegram
Channel created
Channel name was changed to «Go Level Up»
🤍Добро пожаловать в Go Level Up — твой старт в Go-Backend!🥳

💡 Учимся: Осваиваем Go с нуля, разбираем синтаксис, паттерны и лучшие практики.

⚡️ Практикуем: Пишем код вместе, разбираем ошибки, обсуждаем их.

🤖 Моки & Сервисы: Создаем тестовые API, микросервисы и полезные инструменты в командах.

🎯 Развиваемся: Помогаем друг другу расти как backend-команде.

🤔Что здесь будет?

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

📌 Правила просты:

Максимум практики, минимум воды
Делимся знаниями и опытом
Готов прокачаться в Go🧐Поехали! 🚀
Please open Telegram to view this post
VIEW IN TELEGRAM
21💯1
Приветствую всех любителей Go! 🥳

📌Меня зовут Рома, и я разработчик на Go уже больше 5 лет. За это время я не только успел поработать над множеством интересных проектов, но и приобрел ценный опыт, которым хочу поделиться с вами в роли ментора🖱

Почему Go – отличный выбор, и почему вам стоит ко мне присоединиться➡️

➡️Простота и эффективность: Go славится своим чистым и лаконичным синтаксисом, благодаря чему его легко изучать и поддерживать. Этот язык идеально подходит для создания высокопроизводительных и масштабируемых приложений.
➡️Современность: Go был разработан Google с учетом потребностей современной разработки. Он отлично справляется с параллельным программированием, что делает его идеальным для создания распределенных систем.
➡️Востребованность: Go – один из самых востребованных языков на рынке труда. Компании активно ищут Go-разработчиков для работы над облачными сервисами, микросервисной архитектурой и другими передовыми технологиями.
➡️Огромное сообщество: Сообщество Go – одно из самых дружелюбных и активных в мире программирования. Вы всегда найдете помощь, поддержку и вдохновение.

💬Что я предлагаю как ментор:

🔘Индивидуальный подход: Я подстрою программу обучения под ваши цели и уровень подготовки.
🔘Практические навыки: Мы будем работать над реальными проектами, чтобы вы смогли применить свои знания на практике.
🔘Разбор сложных тем: Я помогу вам разобраться с самыми сложными концепциями Go, такими как горутины, каналы, интерфейсы и т.д.
🔘Карьерные советы: Я поделюсь своим опытом работы в индустрии и помогу вам подготовиться к собеседованиям.

🟢Особенности работы со мной:

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

Если вы хотите начать свой путь в мир Go или улучшить свои навыки, я буду рад стать вашим проводником!💻

Пишите мне в личные сообщения, чтобы узнать больше о программе менторства и записаться на пробное занятие.🐈

#go #golang #программирование #менторство #разработка #обучение #google #разработчик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍211
Что выведет код?

func main() {
s := make([]int, 0, 5)
s = append(s, 1, 2, 3)

f := func(sl []int) {
sl = append(sl, 4)
sl[0] = 10
}

f(s)

s = append(s, 5)
fmt.Println(s)
}


#код
Код выведет:
Anonymous Poll
0%
1 2 3 4 5
46%
10 2 3 4 5
26%
10 2 3 5
28%
1 2 3 5
➡️Код выведет: 10 2 3 5

➡️Идем по строчкам:
Создается слайс len=0 и cap=5, далее в него записываем 1, 2, 3. Массив родитель не аллоцировался, так как до cap мы еще не дошли.

📌Функция?! в рамках нее мы принимаем слайс, добавляем 4 и меняем первый элемент. И тут же мы ее вызываем, передавая s. И тут возникает вопрос: s вроде бы ссылочный тип данных, получается передав его в функцию, мы меняем именно его непосредственно по ссылке, ведь так?)

А вот и нет, в го ВСЕ аргументы функции передаются по значению, то есть копируются. Получается внутри функции
sl - копия s
То есть структура состоящая из трех полей len, cap, arr (ссылка на массив родитель) скопировалась

💻Теперь самый сок.
Мы добавили в sl 4 и поменяли первый элемент(помним в голове, что sl ссылается на тот же массив родитель), и получается, что у sl len=4, cap=5. Массив родитель в памяти выглядит так: [10 2 3 4]

Довольные выходим из функции, и приходим к s, у которого len=3, cap=5 (мы же с ним ничего не делали). И получается, что массив родитель [10 2 3 4], но len у него 3, то есть по сути сам s выглядит как [10 2 3]. И как раз-таки при append мы добавляем 5 в ячейку где уже была 4, и перезаписываем ее
🔘(память одна и та же). Таким образом итог вывода в s [10 2 3 5]🤍
Please open Telegram to view this post
VIEW IN TELEGRAM
13🔥21👍1
Хотели бы вы принять участие в бесплатном практическом созвоне, где мы напишем с нуля базовый микросервис на Go?
Anonymous Poll
66%
Да, интересно! Хочу посмотреть на реальный процесс разработки
29%
Возможно, зависит от темы и времени
6%
Нет, не интересно
🦆👎Утиная типизация (Duck Typing)

💡Идея: Если что-то ходит как утка и крякает как утка, то это утка.

💬Очень полезная вещь в продуктовой разработке, регулярно пользуюсь, когда хочу изолировать реализацию сущности (ее поля, от чего зависит и тд) от других сущностей. 💎Особенно полезно, когда пилим сервис и у нас есть слои, так вот по принципам красивой разработки и soLiD🟢 (в данном случае такой подход позволяет придерживаться сразу двух принципов L, D) архитектурные слои должны быть изолированными и независимыми друг от друга, таким образом любой слой очень просто меняется, не затрагивая другие.

Благодаря этому написание моков и подмена слоев на любую другую реализацию (например, юзали библиотеку БД gorm, а в v2-версии слоя решили перейти на самописные SQL запросы) превращается из боли и страданий, в кайф и красоту. На моем опыте я раз 20 переписывал слои сервиса, чтобы их просто замокать и сделать тесты, потому что все завязывались на реализации сущностей😭


type Speaker interface {
Speak() string
}

type Duck struct {
Name string
}

func (d Duck) Speak() string {
return fmt.Sprintf("%s говорит Кря", d.Name)
}

type Pigeon struct {
Name string
}

func (c Pigeon) Speak() string {
return fmt.Sprintf("%s говорит Гули-Гули", c.Name)
}

func main() {
animals := []Speaker{
Duck{Name: "Утка"},
Pigeon{Name: "Голубь"},
}

for _, a := range animals {
a.Speak()
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👏221
Что выведет код?

func main() {
mainSl := []string{"Test", "slices", "copying"}

var sl []string
copy(sl, mainSl)

sl1 := make([]string, 1, 3)
copy(sl1, mainSl)

fmt.Println(sl, sl1)
}


#код
➡️Код выведет: [] [Test]⬅️

Лучшее объяснение этого вывода состоит в идее copy. Предназначена эта функция для копирования элементов слайса-родителя в слайс-ребенок, отвязывая от ссылки на массив родитель
🧐(частая ошибка начинающих), однако делает она это достаточно топорно, она не может ни проинициализировать слайс-ребенок, ни изменить его как-либо, чтобы все элементы родителя скопировались.📌

🖱Всё, что умеет эта функция - глупое перекладывание элементов по ячейкам.

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

Теперь важный момент на понимание слайса: выделенные ячейки - это не cap! Это len! Ровно по этому числу мы понимаем сколько проинициализированных ячеек в слайсе

💬Теперь к результатам:

В sl мы ничего не получили по причине того, что инициализация вида
var sl []string

это инициализация nil-слайса, у которого нет ни длины, ни капасити
🔜copy не смог ничего перетащить

💡Во втором примере у sl1 уже установлена длина 1 и капасити 3, следовательно copy переносит в инициализированную ячейку элемент

Польза copy:
На просторах коммерческой разработки мало встречал использования этой функции, хотя и не понимаю почему
😢 Встречал много решений, где люди копировали слайс через форики, чтобы потом его мутировать/менять и не завязываться двумя слайсами на один массив родитель (опасно!). На мой взгляд, решение громоздкое и некрасивое. copy гораздо эффективнее и лаконичнее, и к тому же оно может также использоваться как получение так называемого ломтика.⬅️

func main() {
sl := []int{1, 2, 3, 4}

part := sl[:2]

partByCopy := make([]int, 2)
copy(partByCopy, sl)

// Изменим sl, чтобы показать наследование ссылки на массив-родитель
sl[0] = 5

fmt.Println(part, partByCopy)
// Получаем вывод:[5 2] [1 2] -> получение базового ломтика, приводит к риску мутаций, в отличие от copy
}
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍22
Давайте согласуем удобное время для мита по написанию микросервиса
Anonymous Poll
19%
Сегодня
15%
25 июня
19%
26 июня
11%
27 июня
67%
Выходные
Что произойдет при запуске программы?

var m = map[string]int{"a": 1}

func main() {
go Read()
time.Sleep(1 * time.Second)
go Write()
time.Sleep(1 * time.Minute)
}

func Read() {
for {
fmt.Println(m["a"])
}
}

func Write() {
for i := 0; ; i++ {
m[fmt.Sprintf("%d", i)] = i
}
}


#код
Голосуем за время воскресного мита
Anonymous Poll
11%
11:00
17%
12:00
44%
17:00
28%
21:00
17%
Не могу в вс🙁
Go Level Up
Что произойдет при запуске программы? var m = map[string]int{"a": 1} func main() { go Read() time.Sleep(1 * time.Second) go Write() time.Sleep(1 * time.Minute) } func Read() { for { fmt.Println(m["a"]) } } func Write() { for i := 0;…
📌При запуске программы произойдет - бесконечный вывод 1 и фатальная ошибка fatal error: concurrent map read and map write

➡️Важно! Данный результат произойдет при версии го, где мапа реализована в виде бакетов, то есть до swiss table. Какой результат будет в новой версии, требует отдельного поста.➡️

💻Итак, вернемся к нашей старой доброй мапе на бакетах. Каждый раз, когда показываю менти этот код, они вчитываются в код и пытаются найти здесь скрытый смысл. Но нет, по сути все, что достаточно увидеть - чтение и запись в мапу из горутин 🔜🔜🔜 ошибка. Дело в том, что мапа не потокобезопасна. 🥳

➡️На моках постоянно отвечают так😳Но не понимают почему так и что это значит. Это значит, что как раз-таки из мапы нельзя читать и писать одновременно в связи с вероятностью обратиться к несуществующему эвакуированному куску памяти или вовсе вызвать гонку данных.

💬Причина: при эвакуации старые бакеты постепенно освобождаются и ключи-значения переходят в новые выделенные бакеты бОльшего количества. Так вот когда мы пишем мы сталкиваемся с риском эвакуации, а когда читаем с риском обратиться к несуществующему удаленному блоку памяти или непредсказуемым данным(из-за data race)🖥
👍321
➡️Итак, фаталы и конкурентные ошибки мапы это классика начинающего Go-разраба💻

🖱Но что делать, когда так хочется заюзать мапу конкурентно, чтобы не блокироваться на вставку/чтение, и при этом не упасть с злосчастным fatal error. 😨

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

📌В данном примере лучше юзать не просто мьютекс, а RWMutex. Разница? Read-Write Mutex позволяет блокировать блок памяти только на запись, и при этом разрешать этот же блок читать.👣

type SafeMap struct {
sync.RWMutex
data map[string]int
}

func main() {
protectedMap := SafeMap{
data: map[string]int{"a": 1},
}

go protectedMap.Read()
time.Sleep(1 * time.Second)
go protectedMap.Write()
time.Sleep(1 * time.Minute)
}

func (sm *SafeMap) Read() {
for {
sm.RLock()
fmt.Println(sm.data["a"])
sm.RUnlock()
}
}

func (sm *SafeMap) Write() {
for i := 0; ; i++ {
sm.Lock()
sm.data[fmt.Sprintf("%d", i)] = i
sm.Unlock()
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
83👍3
Коллеги, спасибо за участие в опросе! Большинство выбрало вариант 17:00. 💻

🖱Сегодня в 17:00 (МСК) созвон — обсудим как писать микросервис на го. Жду всех!📌
Please open Telegram to view this post
VIEW IN TELEGRAM
31👍1