Hello World
Как-то появилась причина попробовать пописать на Go. На тот момент я знал, что это язык от Google, язык молодой, язык компилируемый, вроде как активноразвивающийся и с зарплатами выше средних. Неплохой набор.
В первой попавшейся статье узнаем, что Go к тому же легкий в изучении. Интересно, сколько PHP-программистов стало PHP-программистами, потому что PHP легкий в изучении? И действительно, за пару вечеров можно уже неплохо ориентироваться в языке.
Итак, ищем какой-нибудь golang roadmap, небольшое количество времени, и вот он, helloworld на Golang. Теперь надо его запустить. Сама установка Go - быстрая и простая, занимает пару минут(скачать, нажать далее несколько раз), так что смотрим пример:
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
Первый взгляд
Вместо пространств имен из PHP, здесь пакеты. Смысл немного похожий, но Golang-компоузер идет сразу из коробки. Что еще сразу же бросится в глаза, стоит написать пару строчек кода, так это автоформатирование. PSR, который про оформление кода, также получаем из коробки. И это здорово.
В ознакомительных статьях по Golang можно встретить такое заявление разработчиков: "Если что-то можно сделать на Go, то это можно сделать единственным способом". Громкое утверждение, которое, конечно, не надо воспринимать буквально, но по мере знакомства с языком, можно подмечать такие моменты. Автоформатирование как раз один из них.
Итак у нас есть простая установка, менеджер зависимостей и автоформатирование. Что дальше?
Ошибки
Например работа с ошибками. В Golang нет исключений, зато функции и методы могут возвращать несколько значений. Одно из возвращаемых значений принято использовать для передачи информации об ошибках.
package main
import (
"errors"
"fmt"
)
// f1 returns two values
func f1(arg int) (int, error) {
if arg == 42 {
return -1, errors.New("can't work with 42")
}
return arg + 2, nil
}
func main() {
num, err := f1(42)
if err != nil {
panic(err)
}
fmt.Println(num)
}
Да, как-то так, сюда надо добавить, что если объявить переменную в Go, то ее необходимо использовать, иначе код просто не скомпилируется. Язык подталкивает обрабатывать ошибки, хотя и оставляет возможность этого не делать.
Выше я написал, что в Golang нет исключений, это не совсем так, в Go есть стандартная функция panic, которая при вызове прервет текущее выполнение и начнет "всплывать" наверх по аналогии с исключениями. По аналогии с исключениями panic можно перехватить, обработать и продолжить выполнение программы.
И еще немного про ошибки, вроде язык и подталкивает к их обработке, но также Go предлагает конструкцию пустого идентификатора(Blank identifier), используя его, можно не создавать новую переменную под возвращаемые функцией значения, следовательно, не нужно будет ошибку обрабатывать.
func main() {
num, _ := f1(42)
fmt.Println(num)
}
Получается, если захотеть, то можно и не обрабатывать, но вряд ли это останется незамеченным на каком-нибудь код ревью.
Рутина
Go - язык строготипизированный, плюсы понятны, но это же придется возиться с типами? Да. К счастью, кое-где разработчики Go насыпали сахарку. Например, при создании новой переменной, можно использовать оператор := и тогда указывать её тип не придется.
Второй момент - это работа с массивами. Да, всепоглощающего array из PHP здесь нет, нужно будет вспомнить конструкции вида [][]string, но вот с выделением памяти уже подсобили, в Go есть тип Slice, который сильно упрощает эту задачу.
func main() {
//create slice with len 1
myslice := make([]int, 1)
for i := 1; i < 10; i++ {
// add necessary amount of elements
myslice = append(myslice, i)
}
//print slice with 11 elements
fmt.Println(myslice)
}
А вот удалять неиспользуемые данные будет сборщик мусора. Из рантайма. Из рантайма еще будет безопасность для указателей. Но, собственно, в Go есть рантайм, без него не поедем.
ООП
В Go нет наследования классов, привычного PHP-программисту. Вместо наследования - нечто вроде композиции. При этом мы не можем использовать "наследника" вместо "родителя". Но можем реализовывать интерфейс, и использовать уже его. Получается полиморфизм только на интерфейсах.
Стоит добавить, что реализация интерфейса в Go неявная, не нужно использовать аналог implements, достаточно чтобы "класс" содержал методы из интерфейса. Здесь класс написан в кавычках, потому что в Go нет привычных нам классов. Зато есть структуры и возможность привязывать к ним функции. Вот как это может выглядеть:
// our new type
type Type1 float64
// Golang way for method's definition
func (t1 Type1) Show() {
fmt.Println(t1)
}
// methods and properties of Type1 accessible over Type2
type Type2 struct {
Type1
}
// redefine Show method
func (t2 Type2) Show() {
fmt.Println(t2.Type1 + 7)
}
func myF1(t1 Type1) {
t1.Show()
}
// our new types have implemented this interface just because
// they contain Show method
type Shower interface {
Show()
}
func myF2(s Shower) {
s.Show()
}
func main() {
t1 := Type1(54)
t1.Show() //54
t2 := Type2{Type1: 45}
t2.Show() //52
//we can access method of "parent"
t2.Type1.Show() //45
myF1(t1) //54
// will not be compiled, we cant pass Type2 instead Type1
//Show(t2)
myF2(t1) //54
myF2(t2) //52
}
И про инкапсуляцию, в рамках одного пакета (это не прямая аналогия с пакетами composer'a, пакет в Go - это один или несколько файлов, объединенных по какому-то смыслу. Из пакетов в Golang получается модуль. Модуль - это уже что-то похожее на пакет из composer'а) у нас есть доступ ко всем свойствам и методам наших типов. А при использовании одного пакета в другом пакете будут доступны только свойства и методы начинающиеся с большой буквы. С маленькой буквы - это аналог private. А protected доступа нет, т.к нет наследования.
Многопоточность
Цирковой номер - PHP-программист рассказывает про многопоточность. И все же, многопоточность является одной из выделяемых особенностей Golang. Легко можно создать несколько новых потоков, наладить между ними взаимодействие, установить контроль над данными, чтобы избежать гонки.
Но как я понял из пары-двойки статей про многопоточность - это не палочка-выручалочка по увеличению производительности. Бездумно нарастить количество потоков не получится, даже если твоя задача хорошо распараллеливается, то все может упереться в специфичность окружения, и выигрыш в производительности окажется не таким уж впечатляющим.
И продолжая тему производительности. Хоть язык и компилируемый, но у него есть рантайм со сборщиком мусора и безопасностью. И судя по паре-двойке статей, где сравнивалась производительность Go с другими языками, получается что-то такое: в большинстве приведенных в статьях тестов, Go обгонял интерпретируемые языки, в большинстве тестов был быстрее Java, но ни в одном приведенном тесте не был быстрeе C, C++, Rust.
Сообщество
Язык молодой, людей в нем не то, чтобы много, соответственно готовых решений под твои потребности может и не найтись. Готовых ответов на твой вопрос может и не найтись и т.д. Если в рамках микросервисных задач материалов достаточно, то если отойти в сторону какого-нибудь AI, будет куда сложнее. В последние несколько лет рост Go замедляется.
Выводы
У Golang есть целый ряд плюсов. Один из главнейших, на мой взгляд, это простота в изучении. Много вещей идут сразу из коробки. Язык от Google. Писать на Go просто приятно. Концепции, заложенные авторами, позволяют под новым углом взглянуть на привычные вещи. Но хватит ли этого, чтобы попасть и закрепиться в "топах" - вопрос открытый.
P.S. Подозреваю, что в статье есть неточности или даже откровенные ошибки. Если ты их заметил, помоги тому, кто мог не заметить - оставь комментарий.
Комментарии (62)
savostin
27.09.2023 12:33+2Так и не понял до сих пор почему от него все так "восхищаются". Только из за сборщика мусора? Какой-то C++ для
тупыхленивых...GospodinKolhoznik
27.09.2023 12:33+4Он больше на Паскаль похож, чем на Си, и уж тем более чем на Плюсы.
А вообще кто им восхищается? Многие отзываются о нем презрительно. Нынче модно восхищаться Растом.
Ravager
27.09.2023 12:33раст прикольный, но...помню хотел извлечь значение из мапы(проверить что оно там есть) и проверить его на <= 7. в любом языке это можно сделать в одну строку типа if key in map and map[key] <= 7. в расте я сломался - либо делай матчинг и вложенный if, либо просто 2 вложенных ифа.
Free_ze
27.09.2023 12:33+2Наивный вариант в расте работает аналогичным образом:
if map.contains_key(&key) && map[&key] <= 7 { ... }
Но есть и
get
, который возвращаетOption<&V>
, с которым можно сделать что-то вроде:map.get(&key).map(|value_ref| *value_ref <= 7).unwrap_or(false)
Ravager
27.09.2023 12:33if map.contains_key(&key) && map[&key] <= 7 { ... }
о, спасибо. почему-то нигде не видел про этот оператор, только get.
map.get(&key).map(|value_ref| *value_ref <= 7).unwrap_or(false)
да, это я тоже видел. но так писать не хотелось.
sgaluzin Автор
27.09.2023 12:33+1сложно ответить, видимо основной хайп был от "язык от гугла". на самом деле в языке целый ряд плюсов, и на каждый плюс найдется другой язык, в котором этот плюс лучше. но вот если усреднить, то го выглядит очень хорошо
akaddr
27.09.2023 12:33+7Я на него перешел с С (embedded) и говорю обычно, в нем есть все что не хватало в С - многопоточность, сборщик мусора, управление пакетам, профилирование, тестирование, форматирование средствами языка и т.д, запуск параллельной задачи одним оператором go это мощно
Dancho67
27.09.2023 12:33Так по этому то его и полюбили работадатели. Берем вкатуна с курсов, даем ему неделю и отправляем перекладывать жусоны.
Tuxman
27.09.2023 12:33Time to market (TTM) у Go получается короче, чем на C++, проверено сотнями компаний по всему миру. Плюс на С++ программистов надо брать опытных, а то в ноги себе постреляют, а на Go можно бывших PHP'шников конвертировать за две недели. А в C++ вы за две недели PHP'шников не сконвертируете ;-)
gmtd
27.09.2023 12:33+10myslice := make([]int, 1) ... for i := 1; i < 10; i++ { ... num, _ := f1(42)
Жуткий синтаксис...
Почему нельзя использовать нормальную привычную в большинстве языков запись?
В чем мотив?
rezedent12
27.09.2023 12:33+4Этот язык разрабатывали не для удобства программистов, а для удобства их нанимателей. Точнее что бы низкоквалифицированные программисты не могли использовать запутанные подходы и создавать не отслеживаемые ошибки. Если ошибка игнорируется - это видно, если переменная не используется - то это тоже заметно.
wilcot
27.09.2023 12:33+5С первым примером соглашусь, эта штука действительно выглядит странно, но это больше для тех, кто уже хорошо знаком с языком.
Насчет второго примера не понимаю, в чем претензия? В том, что круглые скобки не используются? Мне так наоборот, бывает непривычно уже эти круглые скобки писать :) На том же C++ такой же цикл пишется так:
for (int i = 1; i < 10; i++) { // ... }
А некоторые любят
++i
писать, даже споры из-за этого бывают, что быстрее. Аналогично "if expr {}
" как-то уже приятнее выглядит чем "if (expr) {}
". Кажется дело привычки.Насчёт 3 примера тоже не понятно. Вроде стандартный синтаксис (например, в том же python вполне нормально написать
a, _ = f1(42)
), чтобы развернуть tuple.Может вам не нравится, что вместо конструкции
var a = b;
пишется простоa := b
? Интересно узнать, чем же привычный для других языков синтаксис тут внезапно стал жутким.
Tuxman
27.09.2023 12:33Предлагаю тему. Написать какой-то абстрактный REST сервис на PHP и на Go. Внутри что-то там повычислять, хотя все эти эти сервисы обычно просто в другие сервисы ходят или в БД запросы делают.
Но мы будем сравнивать PHP скомпилированный, т.е. kphp и скомпилированный код на Go. Кто быстрее? Кто меньше памяти потребляет?
В чём соль? Что PHP+kphp, что Golang, оба решения нацелены на привлечение бакенд-программистов "средней" ценовой категории, с максимально быстрым time-to-market, чтобы сразу в продакшен.
Tony-Sol
27.09.2023 12:33Не надо kphp, лучше php+roadrunner
kphp приемлемо могут готовить пожалуй только во ВКонтакте
MyraJKee
27.09.2023 12:33+1Тоже сейчас потихоньку изучаю. После пхп, тем более после java очень непривычно и вызывает отторжение.
sgaluzin Автор
27.09.2023 12:33Очень интересно как вам го после java, может быть подсветите пару моментов?
MyraJKee
27.09.2023 12:33+1Я пока не сильно погружался, и вряд-ли что-то новое напишу :))
Первое что прям сильно непривычно - очень слабая связанность. Что наверное даёт и свои преимущества. Интересно выглядит написание интерфейса, использование его без реализации, написание под это дело всех необходимых тестов.
Механизм обработки ошибок это какой-то адок. Кажется хотели сделать более явный механизм проверки ошибок, как я понимаю над этим идёт какая-то работа. Жаль что не стали try catch внедрять.
Не нравится что в угоду быстродействию(?) Экономия на спичках. Все вот эти сокращения. На сколько понимаю и переменные рекомендуется именовать кратко.
Все может быть в одном файле - интерфейс, функция, структура. Кажется что в не сильно умелых руках это может стать мощным орудием для создания говнокода.
А в целом кажется java и go нет смысла сравнивать. Совершенно разные яп. Go плохо подходит для энтерпрайз. И хорош для быстрых микросервисов.
AleksandrTm
27.09.2023 12:33На сколько знаю, он в ГО и создавался для микросервисов, но многие его пихают везде где можно и нельзя, такая же история была с python
micronull
27.09.2023 12:33+2вызывает отторжение.
Зачем изучать инструмент, который вызывает отторжение? Вакансий в Go кратно меньше чем в PHP и Java.
MyraJKee
27.09.2023 12:33Думаю может втянусь. Знания не бывают бесполезными. Да и смотрю сейчас достаточно много вакансий где нужен пхп+го, во многих крупных конторах сейчас пишут микросервисы на го
WebMonet
27.09.2023 12:33Более интересный вопрос - куда-бы применить возможности Go? Что-бы такого интересного написать на нем, что на ПХП не получится или получится криво?
sgaluzin Автор
27.09.2023 12:33Нейросеть
WebMonet
27.09.2023 12:33Ух как круто. Но для этого нужно разбираться как минимум в двух вещах - в Golang и нейросетях. Лично я пока новичок и в первом и во втором. Мои мысли идут пока в сторону простеньких текстовых игр, какого-нибудь сервера, стримающего mp3 в приложеньку или каких-нибудь небольших утилит (парсинг, конвертация данных) и т.д.
micronull
27.09.2023 12:33+2что на ПХП не получится или получится криво?
Маленькие и лёгкие приложения в один бинарь, без зависимостей от сторонних библиотек и интерпретаторов.
gmtd
27.09.2023 12:33Это самоцель такая?
Где в современном мире такие вещи используются, не подскажите? Чтобы представлять целевую аудиторию.
micronull
27.09.2023 12:33Где в современном мире такие вещи используются, не подскажите?
gmtd
27.09.2023 12:33-
Зачем микросервису маленький легкий "бинарь", если хоть Java Spring Boot, хоть PHP выполняют ту же задачу так же одинаково или лучше?
Как в маленький легкий "бинарь" поместиться библиотека работы с MySQL например, или AWS S3? Или для чего микросервисы используются? Маленький и легкий?
micronull
27.09.2023 12:33Маленький и легкий?
По сравнению с современными приложениями, например на электроне, это действительно лёгкий.
выполняют ту же задачу так же одинаково или лучше?
Не спорю что PHP или Java лучше в своей нише.
Как в маленький легкий "бинарь" поместиться библиотека работы с MySQL например, или AWS S3?
А ещё там http сервер раздающий статику из того же бинаря помещённого с помощью https://pkg.go.dev/embed
-
tommyangelo27
27.09.2023 12:33Я писал для себя утилиту скачивания фоток через апи. Почему го а не пхп? Проще многопоток организовать, я сразу 10 горутин запускаю, апи позволяет.
gmtd
27.09.2023 12:33Конкретно в данном случае есть multi curl в php
Но в принципе - да. Многопоточность если нужна, то надо смотреть не в сторону PHP или JavaSript
MyraJKee
27.09.2023 12:33+1У нас в энтерпрайзе это микросервисы. Они как ни крути быстрее пхп, потребляют куда меньше ресурсов чем java
Zuy
27.09.2023 12:33Софт для какого-нибудь embedded linux устройства, где кроме минимальной rootfs больше ничего нет.
Ravager
27.09.2023 12:33+1Что-бы такого интересного написать на нем, что на ПХП не получится или получится криво?
единственное что в го делается красиво это многопоточка. в остальном язык полностью убог. если что пишу 8 лет на нем.
olivera507224
27.09.2023 12:33+1Например, при создании новой переменной, можно использовать оператор := и тогда указывать её тип не придется.
Не так. Точнее, так, но не совсем. Го умеет выводить типы, поэтому дефолтная конструкция объявления с присваиванием
var a: int = 42
превращается в
var a = 42
И именно в этой конструкции опускается тип объявленной переменной. Для того чтобы сократить и эту запись, имеется уже означенный автором сахар:
a := 42
Всё это работает только для объявления с присваиванием. Если нужно просто объявить переменную, то без явного указания типа уже не обойтись:
var a: int
P.S. Как человек, много писавший на C#, JS и TS, а в настоящий момент часто пишущий на Python, Lua, PHP и Go, могу сказать что среди перечисленных языков Go выделяется довольно скверным синтаксисом. От нагромождения скобок слезятся глаза, отсутствие лаконичной записи обработки ошибок приводит к рассеиванию внимания на куче блоков if при чтении кода, а неявная реализация интерфейсов не позволяет узнать заранее, какой интерфейс реализует тип. И это далеко не весь список проблем. Но. Всё же синтаксис хоть и громоздкий, он ощущается весьма лаконичным, особенно при включённом автоформате. Реализация конкурентности на мой взгляд лучшая среди перечисленных мной языков - если есть большой объём данных, который можно обработать конкуретно, я без капли сомнения выберу Go. В целом язык ощущается минималистичным по возможностям и ключевым словам. Когда я только начинал с ним знакомиться, у меня было такое ощущение, словно меня заперли в палате, стены которой обиты матрацами. Но вместе с тем эта минимальность каким-то образом соседствует с богатыми возможностями, которые предлагает стандартная библиотека. Можно сказать, что Go я и люблю, и терпеть его не могу.
sgaluzin Автор
27.09.2023 12:33Спасибо за замечания. По поводу if на ошибках, я видел, что проверку ошибок оборачивают в Must, внутри, если ошибка - падаем с panic, а если ошибки нет - возвращаем значение. Но да, это далеко не все кейсы покроет
olivera507224
27.09.2023 12:33Соль в том, что 99,9% ошибок никак не должны приводить к панике, они должны быть именно обработаны. Паника - это крайний случай, я её обычно вызываю только в функции main, если не завелось что-то очень критичное.
itmind
27.09.2023 12:33+2По моему мнению громоздкий синтаксис у C#, Java, Kotlin, C++
Пример синтаксиса на C# (на Java похоже)
[Anotation1] [Anotation2(Enum.Param)] public static void TryAddSingleton<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this IServiceCollection collection) where TService : class where TImplementation : class, TService {...}
У Kotlin плюс ко всему его DSL, когда вызов функции пишется как объявление функции (и получаются многоярусные вложения на несколько экранов)
val appModule = module { single<UserRepository> { UserRepositoryImpl().apply{ this.param = MyClass.GetParam() } } }
А на Golang исходный код проще всего читать, он понятнее, синтаксические конструкции прощее. Минимализм конструкций языка позволяет после пары часов изучения уже писать достаточно качественное ПО.
В Kotlin же например только 5 scope function: let, run, with, apply, и also. И программист глубоко не знакомый с языком, читая код не поймет, что там происходит? не прочитав документацию.
olivera507224
27.09.2023 12:33Ну, я описал собственные впечатления, в плане синтаксиса я бы вообще отдал предпочтение Lua.
По поводу Котлина полностью поддерживаю - запись вызова функции, которая выглядит как её объявление, вводит в ступор, подобный сахар кажется просто излишним. Такой записью грешат ещё и, если не ошибаюсь, Swift и Ruby, но в последнем передаваемая лямбда хотя бы визуально выделяется ключевым словом do.
mr-garrick
27.09.2023 12:33+2Синтаксис не от мира сего, вообще никакой логики. Говорят "можно сделать только одним способом" и тут же вам сразу четыре варианта объявления одной и той же переменной. Про типы вообще непонятно зачем их методы не внутри типа, а снаружи костылями подпёрты. Странная, непривычная, не такая как у других, лексика, названия элементов языка - слайсы вместо списков, горутины вместо потоков или тасков и т.п. Обработка ошибок... сразу видно потом сбоку прикрутили, так чтобы не сломать предыдущее, не очень удачно кмк. Интерфейсы... ну, вот нельзя было в объявлении переменной указать что она сделана из интерфейса? Как в большом проекте глядя в исходник узнать что это вообще из интерфейса и из какого? никак - фиг найдёшь. Вообше большие проекты на Go бывают?
viktorprogger
27.09.2023 12:33+4Как PHP-программист, первые недели работающий с Golang, хочу свои 5 копеек вставить.
У структур нет единой точки создания объекта, только кастомные статические конструкторы, которые использовать необязательно. Что ведёт либо к потенциальным ошибкам, либо к огромному количеству проверок на корректность заполнения полей.
Объекты-ошибки вместо исключений есть, но благодаря их обработкам код становится в среднем в 1.5 раза длиннее, чем аналогичный на PHP. Это мой опыт в моем проекте, у других людей разница может быть другой. Но она будет.
Категорически не согласен с фразой о строгой типизации языка. В нем присутствует утиная типизация, а не строгая. Твой интерфейс может случайно совпасть по сигнатуре с каким-нибудь другим, и реализующий один из этих интерфейсов класс будет реализовывать их оба, без вариантов. Либо метод структуры случайно совпадет по сигнатуре с каким-нибудь интерфейсом.
В Го нет nullable типов, но ссылки всегда могут указывать на nil. Благодаря этому даже не проинициализированная переменная любого типа содержит значение этого типа. Integer содержит 0, объект структуры - всю структуру. И нет никакого способа узнать, 0 в переменной лежит потому, что его туда сознательно положили, или это дефолтное значение.
Я отнюдь не хочу сказать, что один язык хуже или лучше другого. Они для разных целей. Го из коробки умеет быстро мапить json на структуры и делать крутую многопоточность благодаря горутинам. Моя первая программа на го - парсер сайта аренды квартир, который я написал за 15-30 минут (благодаря помощи ChatGPT :D). Но я бы не стал брать Го для вещей, где нужно описывать бизнес-логику. Уж лучше PHP: "правоверный" ООП для этого больше подходит и оставляет меньше места для ошибок.
Ravager
27.09.2023 12:33У структур нет единой точки создания объекта, только кастомные статические конструкторы,
Объекты-ошибки вместо исключений есть, но благодаря их обработкам код становится в среднем в 1.5 раза длиннее
В Го нет nullable типов, но ссылки всегда могут указывать на nil.
просто вы думаете что go это ооп язык, и сравниваете его с java, php и подобным. но это зря. go это си с GC и всеми вытекающими.
Категорически не согласен с фразой о строгой типизации языка. В нем присутствует утиная типизация, а не строгая.
утиная типизация относится лишь к контрактам. попробуйте сложить float + int без приведения и узнаете насколько типизация строгая.
viktorprogger
27.09.2023 12:33просто вы думаете что go это ооп язык, и сравниваете его с java, php и подобным
Все верно: я PHP-разработчик, а Go - новый для меня язык. Я сравниваю его с тем, к чему привык. Я понимаю, что там нет ООП, но речь не об ООП как явлении, а об оставленных возможностях. Например, создание объекта из структуры без заполнения всех обязательных полей. В PHP я бы был зол на того, кто так пишет) Но это не PHP, все верно, и ООП тут нет. Поэтому не стоит использовать этот язык там, где ООП было бы уместнее.
Ravager
27.09.2023 12:33все верно, ломать будет сильно. я писал и на Си и на плюсах. и хоть там тоже можно писать в строго процедурном стиле - все равно язык непривычен. не хватает конструкторов, исключений да и вообще выразительности. писать бизнес логику на нем просто ломка жуткая.
rezedent12
Посоветуй хорошее руководство-учебник. (На русском языке)
noRoman
не совсем учебники
https://gobyexample.com/
https://tour.ardanlabs.com/tour/eng/list
pfemidi
Нисколько не рекламы ради, я к ним вообще никакого отношения не имею, но для начала, именно для начала IMHO данный курс вполне подходит.
avkritsky
Так же не для рекламы. В настоящий момент прохожу указанный курс. Для "пощупать/покрутить" - очень здорово. Но именно для начала, всё же поверхностно некоторые вопросы проходятся.
sgaluzin Автор
сорь, это не на русском, но мне понравился формат роадмапки. тут кем-то хорошим собраны темы в порядке важности и к ним прикреплены ссылки на несколько источников. попробуй найти аналог на русском https://roadmap.sh/golang