Golang - о его недостатках
На Golang я перешел с Java больше 5 лет назад - в целом это по индустрии даже достаточно заметный и оправданный переход. Но местами он бесит. Буду записыавть себе на память и тыкать в нос собеседующим - бывает нужно :)
В 2020м про Golang многие разве что слышали - а сейчас на нём пишут примерно столько же сколько на Java/C# например. Основная мотивация - Java была сделана ради "compile once run everywhere" - но мы уже давно пришли к тому что в серверной, бэкендовой разработке это не нужно. Бэкенд практически всегда это кластер под линуксом, с кубернетесом для оркестрации всего хозяйства.
А значит - выгодно компилировать нативно - и быстрее, и память не прожирать в таких объёмах. Экономия на инфраструктуре - вот что обуславливает внедрение Go. Ну и то что он достаточно удобен. Это не современный C++ (вынос мозга) и не Rust (тоже вынос), не Haskell (где ты, Биокад?) - вообще компилируемых нативно языков для этой цели было не много (интересный момент разве что с Kotlin связан).
Ну и что ж - прошло 5 лет, выпустилось много новых версий Go - а некоторые моменты остались такими что заставляют программистов городить жуткие конструкции и смотреть на все это - глаза блюют.
1) Тернарный оператор - пресловутый. Его в Go нет. Вместо одной строчки нужно писать 4 или 5. Утверждается что авторы языка считают что этот оператор может запутывать. Тем не менее просьбы внедрить его появляются в issues на гитхабе почти ежемесячно. На самом деле, вроде, проблема в том что в промежуточном коде который генерируется из AST не заложено подходящих выражений в которых этот оператор можно описать. Если попытаться реализовать это в исходниках Go - так или иначе натыкаешься. Можно намудрить с анонимными функциями, но скоуп будет не вполне соответствующий.
2) Каналы фиксированного размера. Порой это боль. Канал это по сути потокобезопасная очередь - они существуют практически во всех языках. Но только в Go им нужно строго задать размер при создании. Разработчики считают что это сподвигает использовать принцип "backpressure" - если в канал не лезет, значит публикующая сторона должна приостановиться. Резонно но не всегда логично - например, если мы скидываем в канал сообщения о возникающих в системе ошибках - мы не можем ждать или вернуть ошибку. В этой ситуации очередь бесконечного размера - правильное решение. Если она переполнится - приложение будет убито по OOM - и это правильно - пусть за памятью следит система, на этом уровне и мониторинг обычно есть. А внутри кода решать что 100 сообщений накопить можно, а больше нет - это всегда софистика. Написал крохотную либу реализующую функционал на смеси канала и связного списка - но к сожалению полностью совместимым с хитрым синтаксисом Go это решение не сделать.
3) Отсутствие исключений. Почти. В Go "идиоматически" считается возвращать ошибку вместе с результатом, вроде кортежа. Мотивация такая что это ведет к более ясному коду. К сожалению мы, программисты, любим оборачивать функции в функции - всё это позволяет аккуратнее факторизовать код - но получается что ошибку нужно проверить и вернуть на каждом уровне, да еще зачастую как-то её обернуть. Как следствие - Go-шники часто предпочитают писать функции-портянки на сотни строк, лишь бы лишний раз не заморачиваться. Есть ещё паники (и я их использую в качестве исключений например при написании кода всевозможных серверов - если связь оборвалась а протокол не предусматривает пересоединения с сохранением состояния - то нет смысла оборачивать эту ошибку в десятки слоёв.
Как сказал классик "к сожалению здесь недостаточно места", но думаю что добавлю ещё замечаний в комменты или новым постом. А может быть кто-то ещё взглянув на этот пост вспомнит какую-нибудь свою любимую боль.