Как стать автором
Обновить

Комментарии 168

непонятно как без всех этих дженериков и прочих "конструкций" вообще смогли появиться, например, linux kernel. или это еще недостаточно большой проект?

linux kernel это совсем другая эпоха программирования. сейчас эпоха скорости и удобства в написании кода ))

Странно, в статье тоже говорится об "экономии времени разработчика".

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

так а в чем проблема-то? на Go код пишется намного быстрей и удобнее чем например на C, а на более новых языках можно писать еще быстрее и удобнее чем на Go.

если бы не было эволюции экосистемы программирования, то все бы до сих пор писали на двух-трех языках со времен их создания.

в программировании ИИ помогает получать быстрое решение того, что раньше нужно было гуглить или рыть в мануалах.

Хорошо бы примеры кода для сравнения, на C, C++, Rust и Go.

Пример кода? В смысле привести код распределенной системы оперирующей петабайтами данных?

Мне кажется, дело в темпе разработки - условный Linux kernel разрабатывался энтузиастами в свободное время, как я понимаю, поэтому они могли себе позволить отвести на разработку больше времени и, соответственно, многословная разработка не была проблемой. В то же время, в разработке для бизнеса сроки сжатые, возможно поэтому есть тенденция писать на более выразительных языках

я с этим тоже согласен

условный Linux kernel разрабатывался энтузиастами в свободное время

это не так уже больше 15и лет. Ядро разрабатывается преимущественно сотрудниками крупных корпораций в рабочее время. Инфраструктура так же работает за счёт корпораций.

Даже энтузиастам надо на что-то жить. И соответственно тратить время на заработки, на другую жизнь, на прочая. Спрашивается: так ли много у них свободного времени в принципе?

В golang прям сильно не хватает устоявшихся практик. Плюс когда на нем начинаешь реализовывать условно clean architecure или DI хотя бы, простота языка начинает висеть гирей.

linux kernel вообще изначально был специально спроектирован без использования ООП. Думаю, что сравнивать низкоуровневые структуры ядра и бизнес-объекты энтерпрайза вообще некорректно, это абсолютно разные уровни, требующие разных подходов, вне зависимости от конечного количества строк кода в проекте.

Можно подумать в ядре нет ООП

в ядре линукса - нет

был не прав, есть

Не равнозначное сравнение. Любой сервис web приложения и Linux kernel это абсолютно разные уровни, в частоте решения задач, в скорости разработки, удобстве разработки, в частоте расширения, в количестве миддл участников.

OS Kernel это другой тип кода совсем. Мы же не будем ставить на первое место скорость и удобство разработки в, к примеру, базах данных? Стабильность, надежность и долговечность здесь важнее.

Линус вообще топит за чистый Си.

Ядро линукс- это 1) ядро операционной системы стационарной вычислительной машины 2) написана во времена, когда растов и го не было в помине 3) язык С никакой Раст не превзойдет по скорости 4) но С хреново подходит для современного скажем так корпоративного софта. Нет, не потому что он плохой, он как раз хороший, но стоимость разработки на нем - временная и финансовая- просто огромная, плюс хорошо писать на С надо учиться долго, программистов не наберёся

go появился в 2007-2009 году, хотя да, ядро было раньше.

Чистый си достаточно лаконичен и легко читается и пишется, особенно когда ООП в разумных пределах, но есть недостатки типа нет обработки исключений (так было лет 10 назад, когда папа, работая на французскую фирму, использовал си++ в режиме "си плюс пара плюшек", для OCR нужна была производительность и баланс по сложности и скорости). Тогда как "нативный" с++ это "неймспейсы классов неймспейсов", код просто чудовищно читаемый и неподдерживаемый, пример voip либа под телеграм. Пытался написать некую либу.. При этом в универе учили си 3 года, жаву год и с++ год, помимо всяких ассемблеров. Так вот, на си писать - требуется правильный склад ума, но читается он любым человеком, кто умеет в любой другой язык.

Вопрос уровня "как без автокадов и планшетов королёв человека в космос запустил или это ещё недостаточно сложный проект?"

Спасибо за статью, интересное, особенно в части GC, теперь ещё больше хочется "потрогать" Rust. Но удивило следующее: `Go недостаёт: мощной поддержки IDE` (c). Typescript - это конечно, хорошо, но где поддержка TS лучше, чем go? VSCode? `Goland` от jetbrains просто "божественно" определяет все объявления, использование и имплементации, даже в сорсах, в отличии от `Webstorm` + TyepScript и других связок. Моё мнение, что данная характеристика напрямую зависит от выбранной IDE и косвенно от самого ЯП.

меня это тоже удивило в оригинальной статье, но я не имею такого опыта с TypeScript, чтобы оспорить этот момент ))

За Goland вроде как денег хотят. А дефолтный LSP у Go.... скажем так бесит больше, чем помогает. Он абсолютно не умеет работать с недописанным кодом, одна опечатка и все умные функции отключаются

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

Но вообще хороший такой наброс, нажористый. Я даже от себя добавлю

Скрытый текст
Клавиатура программиста на Go
Клавиатура программиста на Go

я ключевые слова не добавлял. всё переведено "один в один" с небольшими правками стилистики.

Это даже не перевод. Просто скопипастили в нейронку и ее выхлоп дальше скопипастили на хабр. Без всякой обработки

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

я это говорю вам как лингвист изучавший 5 иностранных языков, и сейчас изучающий 6й язык.

не надо проецировать свой личный опыт на других, пожалуйста!

А пунктуацию в этих пяти языках пока не изучали?)

Простите, не смог удержаться)

Пунктуация и переносы это маркер иноязычного автора, даже если всё остальное в тексте прекрасно.

видимо не изучал, так как не понимаю на какую проблему с пунктуацией вы намекаете ))

Если позволите.

Вам может показаться, что так и есть, (сложносочинённое предложение) но это полный перевод с построчной сверкой с оригиналом. Естественно, (вводное слово) нейросети помогают ускорить процесс перевода, который просто вручную занимает в 5 раз дольше.

Я это говорю вам как лингвист, (причастный оборот) изучавший 5 иностранных языков и (соединены союзом) сейчас изучающий 6-й (смешанный способ оформления числительных) язык.

Видимо, (вводное слово) не изучал, (обособление придаточного изъяснительного) так как не понимаю, на какую проблему с пунктуацией вы намекаете.

да, верно, у меня в школе была по русскому тройка, а по иностранным и математике пятерки ))

Нейронка так не переведёт. Текст немного лучше чистого джипити.

Авторы, а пожалуйста делайте тексты так, чтобы не видно было, что за вас их готовит llm. Вот эти все встреченные буллеты в списках, выделенные ключевые фразы и все прочие признаки нейронок заставляют на уровне рефлексов пролистать текст до конца на быстром режиме, чтобы поставить минус за статью. Зачем оно вам надо?

Вот это вообще шлак полный, конечно
Вот это вообще шлак полный, конечно

конкретно написано, что это перевод статьи!

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

Не понял, вы через ллм прогнали выхлоп другой ллм? А в чем ценность статьи тогда?

ценность статьи только лишь в ее содержании. ллм помогает, но не заменяет
"трудности перевода".

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

У меня вообще ощущение, что на хабре живые люди уже не пишут.

Вы правы! Иногда действительно кажется, что статьи и комментарии пишут роботы или нейросети — настолько всё стало шаблонным и безликим. Но живые люди ещё есть! Просто многие, наверное, устали от токсичности или переключились в режим «читаю, но не комментирую». Лично я вот стараюсь писать по-человечески — может, если таких станет больше, атмосфера снова изменится в лучшую сторону. 😊 А вам как кажется — что могло бы вернуть Хабре «живое» общение?

Это написано ллмкой :)

и зачем вам тратить время на эти комментарии не посмотрев оригинал?

... паузы GC неприемлемы. Именно поэтому крупные компании всё чаще выбирают Rust (или даже C++) в этих средах

А что, когда-то было иначе? Где-то, где важен каждый такт, были языки с ГЦ? По личному опыту, там всегда сидели плюсы, которые сейчас вытесняются растом.

Ну а Го осел в нишах, где нужна параллельность-конкуррентность

Не, ладно, дженерики, но когда я узнал, что в Go надо после каждой строчки кода проверять err != null - это ж застрелиться можно

Гораздо веселее читать явовские стек трейсы на три экрана, правда ведь?

В яве хочель портянку из ифов делай, хочешь пробрасывай - лови. Хочешь любым другим способом. А стектрейс наоборот удобная штука - сразу показывает где проблема, еще и в строчку ткнет

Их читать 5 минут. Мало того, хорошо бы иметь автотесты, чтобы читать реже пришлось, это не зависимо от используемого языка.

А вот размазать 100500 проверок ошибки на null по всему крупному проекту, как масло на хлеб - это уйма времени и неиллюзорный шанс где-нибудь забыть это сделать.

Централизованного перехвата ошибки я так понял нет. Но при этом с Яве / C#, если есть острая необходимость поманьячить - можно сделать как в Go.

Ну вообще есть, просто в Go нужно иметь понимание ошибок, которые нужно именно обработать и которые должны прервать выполнение(panic). Если очень надо именно выбросить ошибку вверх по стеку, паникуйте и отлавливайте ее централизовано

Джавовые стектрейсы ванлав. По ним можно понять про место ошибки все. И кто кинул и кто вызвал и что между ними было. Дебаг становится заметно проще.

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

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

Как контекст ошибки по трейсу понимать будете?

добавляем необходимые поля в кастомный класс исключения

Ну т.е. колл-стеков для нормальной диагностики ошибок не достаточно и придется вокруг вызываемого метода писать try-catch чтобы заврапить ошибку в кастомный класс исключения. Чем это лучше кода типа?

result, err := geocodingClient.GetPosition(cityName)
if err != nil {
	return nil, fmt.Errorf("get position '%s': %w", cityName, err)
}

На практике, я думаю, враппинг всего в try-catch довольно утомителен и вы просто его не делаете, а восстановление контекста базируется на надежде что что-то полезное будет в соседних записях логов или в текущем span если есть structured logging. Ну или будет "фиг его знает, почему тут вылетел InsufficientPermissionsException, придется дебажить".

Кастомный тип ошибок(ака исключение нужно типа) удобен хотя бы тем что его можно сматчить, там где это необходимо. Например в тестах.

Такую конструкцию как вы указали, проверять в тестах сомнительно, ибо можно проверить только по тексту. В ином случае тоже будет кастомная ошибка и потом её враппинг с доп контекстом. Но даже при этом проверить например cityName будет сложно в тестах.

Такую конструкцию как вы указали, проверять в тестах сомнительно, ибо можно проверить только по тексту.

errors.As

errors.Is

Я об этом написал в своем комментарии

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

В приведенном же коде, с fmt.Errorf, проверить можно только ту ошибку что вернётся из GetPosition

Прочитал ваш комментарий не внимательно. Действительно, проверить ошибку обернутую с помощью fmt.Errorf нельзя.
Она годится для для быстрого расширения контекста там, где нам не надо проверять типы. В большинстве случаев этого более чем достаточно.

В иных случаях нужно что-то типа:

type gError struct {
	err  error
	code codes.Code
}

func (e gError) Error() string {
	return e.err.Error()
}

func (e gError) GRPCStatus() *status.Status {
	return status.New(e.code, e.err.Error())
}

func (e gError) Unwrap() error {
	return e.err
}

Ничто не мешает в го иметь кастомный тип ошибки, если он нужен. В го в качестве ошибки вы можете возвращать любую структуру, которая реализует интерфейс error. Другой дело, что сама необходимость матчинга с исключения с типом часто возникает потому, что висит какой-то общий catch, который по определению не знает откуда исключение было выброшенно и в нем на тип исключения вешают логику вместо того, чтобы написать обработку посредственно там, где ошибка возникла без всяких угадываний.

Можно подумать, с Go-шных return nil, err будет больше информации.
Получение нормального контекста ошибок всегда требует определенной работы.
Что в Golang, что в Java, что в Rust-е.

Но факт в том, что с Golang практически каждая строчка кода сопровождается проверкой err != nil. И это бесит.
В Java с try-catch все-таки кода меньше на выходе. Даже если это просто проброс ошибки дальше, а не вдумчивая обработка.
В Rust с его Result вообще отдельно от метода можно преобразование ошибок сделать - чисто на описании типа и его конвертациях. Сам метод еще чище будет. Но читаемость страдает, на мой взгляд.

Можно подумать, с Go-шных return nil, err будет больше информации.

Так не пишит так - https://github.com/uber-go/guide/blob/master/style.md#handle-errors-once

Golang практически каждая строчка кода сопровождается проверкой err != nil. И это бесит. В Java с try-catch все-таки кода меньше на выходе

Ну т.е. с го приходится думать что ты пишешь и это бесит. Понимаю.

В Rust с его Result вообще отдельно от метода можно преобразование ошибок сделать - чисто на описании типа и его конвертациях

Можно, только на практике 99% проектов будут через anyhow&thiserror и для нормальной диагностики надо будет добавлять with_context(|| ...) на вызовы с Result.

Типичная обработка ошибки - залогировали ошибку, откатили транзакцию, работаем дальше.

В абсолютном большинстве случаев этого достаточно. Контекст вызова есть в месте логгирования, если этого мало для диагностики - всегда можно дополнить ошибку.

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

Если так хочется думать и контролировать каждый чих - можно и к assembler-у вернуться. Контроль всего и вся на 100%.

Ну т.е. колл-стеков для нормальной диагностики ошибок не достаточно

практически всегда достаточно

и придется

практически никогда не придется

Тут не нужно теоретизировать, нужно открыть любой реальный проект на джаве и посмотреть, пишутся там try-catch вокруг каждого вызываемого метода или нет)

практически всегда достаточно

Все так, и tracing со span'ми был добавлен в spring именно потому что колл-стеков практически всегда достаточно для диагностики.

ниче не понял, какая связь вообще) скажите, вы реально пишете на джаве и испытываете трудности, или просто где-то что-то слышали?

ниче не понял, какая связь вообще)

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

скажите, вы реально пишете на джаве и испытываете трудности, или просто где-то что-то слышали?

Я не испытываю трудностей потому что уже много лет не пишу на джаве. А вы пишете на го или "просто слышали"?

Какое отношение распределённые системы имеют к способу обработки ошибок? Распределённость накладывает свои проблемы поверх любого подхода.

Контекст не всегда нужен. Огромный класс сетевых ошибок отлично обходится без него в большинстве случаев. Кто куда ходил по сети понятно просто по стектрейсу при нормальном коде.

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

Так имба же! Меньше чем через год разработки на Java, появляется навык выделять ключевое достаточно быстро (а может дело в том, что IntelliJ Idea подсвечивает синим то, что относится к коду приложения, и явно ненужные вещи сворачивает(omit)), к тому же если все сложно, то можно весь стектрейс просмотреть, чтобы восстановить картину. В Java с концепцией проверяемых исключений всё неоднозначно, но и это поправимо со SneakyThrows

Пишешь функцию must(<твоя функция>)

интересно однако

Все вот говорят, что им не нравится err != null, а покажите пожалуйста, языки в которых архитектурно нет необходимости проверки возвращаемых исключений с возможностью продолжения работы с учётом невалидных данных в переменных? Или большинство ловит краши и считает это нормальным?

Я думаю, никто не говорит, что не надо проверять ошибки. Просто (условно), в Go нужно перепроверять ошибку после каждого шага, потому что если вызываемая функция вернула ошибку, а следующая за неё завершилась корректно - err станет nil. Переодически ловлю такие случаи. В других же языках можно было бы бросить/поймать исключение уровнем выше, если хотя бы один вызов завершился некорректно.

И к чему это приводит?
Я имею в виду вот это самое: "бросить/поймать исключение уровнем выше".

Сам видел когда ловят в main, а дальше все без обработки хоть единой возможной ошибки 100500 строк кода. И когда там ловится то потом замаешься пониами что пошло не так.

Ну блин, тут классическое "заставь дурака молиться". Понятно, что не стоит оборачивать всю программу в try-catch, но если я делаю что-нибудь большое с сетью, но в рамках одной операции (допустим, в цикле скачиваю файлы), мне гораздо проще поймать TimeoutException один на всю функцию загрузки, чем после каждого файла в теле цикла проверять "а не было ли тут таймаута?". И это первое что в голову пришло, таких кейсов - море

Да я ни разу и не спорю. Использование с умом и по месту любой механизм - рабочий. Но есть тонкая разница между "можно так, а можно на потом", и когда тебя, довольно грубо (не спорю), но сразу заставляют подумать "а что если?".

Причем в разработке объемного решения никто не мешает в первой версии просто написать: if err != nil { panic(err) } в месте где "ну вот сейчас я не хочу думать об этом, подумаю после того, как допишу основную логику". А вот когда оно паникнет (а это случится как правило еще до прода) тогда уже подумать, а как же это нужно обработать.

Кто же потом такое переписывает? Работает - не трогай. Вот и оказываются большие проекты с непойми как и где разбросанной логикой обработки исключений среди бизнес логики. И это не программисты плохие это язык плохой.

включаете рул на линтере, получаете список ошибок по использованию panic(err) и исправляете.

И бездумно исправляете на что-то одинаковое другое. Вникать в контекст нет ни времени ни смысла.

Вникать в контекст нет ни времени ни смысла.

Тяжело вам приходится.

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

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

Напр. я читаю записи из БД, и если они проваливаются на одном из шагов, то в Питоне я проброшу исключение наверх и сделаю центральный обработчик для этой группы через try/except, а в Go мне приходится делать обработку на месте для каждой возможной ошибки, и все они будут просто возвращать nil,err , из-за чего только кода с возвращением ошибки на экране становится больше, чем полезной нагрузки, и потом ещё поди разбери, что откуда вернулось.

все они будут просто возвращать nil,err

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

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

data, err := readInt(columnName)

	if err != nil {
		if errors.Is(err, db.ErrInvalidType) {
			return nil, fmt.Errorf("read column '%s' as int: %w", columnName, err)
		}
		return nil, fmt.Errorf("read column '%s': %w", columnName, err)
	}

И так 42 раза. Для всех колонок. И кому оно надо?

если вы читаете 42 колонки в ручную, а не обощенным кодом, то у вас проблемы в коде посерьезней чем бойлерплейт обработки ошибок.

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

У нас в компании вроде довольно строгие линтеры, но они на такое не ругаются. И нам, в целом, не нужна вся эта тягомотина с типами ошибок и т.д., потому что обрабатываются они в бекэнде одинаково, поэтому мы пользуемся более простым алгоритмом - добавляем какой-то описательный текст и выбрасываем ошибку в ответе сервера. Этого УЖЕ достаточно чтобы понять, какая ошибка и где произошла, однако, если посмотреть на одну из функций, то там одних if err != nil {return fmt.Errorf(...)} получается 20 строк в функции на 65 строк в сумме (один из примеров). Если много вызовов в сторонние API, то порой больше половины строк - возврат разнообразных ошибок.

То, что Вы описываете на этот раз - идеальный случай, где везде нужен контекст, на него разные реакции и т.д. - и даже в этом случае обработка ошибок размазывается на 1000 мест в коде, и за всем этим следить совершенно неудобно.

Вы сами пишите "добавляем какой-то описательный текст", "{return fmt.Errorf(. ". Как это противоречит "return nil,err это антипаттерн"? Я же не говорю о типизированных ошибках, я говорю о добавлении контекста.

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

Таких или подобных ситуаций очень много, и какой-нибудь классический try/except как в Питоне гораздо удобнее, когда обработку исключений можно централизовать, и не быть вынужденным размазывать обработку ошибок для КАЖДОГО вызова функции.

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

В других языках есть более удобные, гибкие и - главное - композируемые способы решать эту проблему

в Go должны однажды решить эту проблему...

я вижу периодически делают предложения, но они не проходят.

вот например интересный пакет errless: https://github.com/mfatihercik/errless

Я посмотрел примеры по ссылке. Подскажите, как вы решаете первый пример в других языках без отлавливания ошибок на возврате результатов функций?

этот пакет errless не пример красивого решения проблемы перехвата ошибок в Go, а всего лишь чья-то креативная попытка реализовать подобие того, что в других языках лежит в основе ))

Это понятно, но все же есть кейс с преобразованием строки к int и обратно. Как решается этот кейс без явного отлавливания ошибок в других языках?

https://doc.rust-lang.org/std/result/
https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-result/
https://www.scala-lang.org/api/3.x/scala/util/Either.html
https://hackage.haskell.org/package/nri-prelude-0.6.0.6/docs/Result.html

обычно совместно с подобным механизмом идёт запрет бросать исключения где попало (в той или иной форме)

пример для Rust

let string_value = "123";
let conversion_result = i32::try_from(string_value.parse::<i64>().unwrap());
    
if let Ok(number) = conversion_result {
    println!("Number: {}", number);
} else {
    println!("Failed to convert");
}

Kotlin

try {
    val stringValue = "123"
    val number = stringValue.toInt()
    println("Successfully converted to: $number")
} catch (e: NumberFormatException) {
    println("Conversion error: ${e.message}")
}

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

На rust я не вижу принципиального отличия от конструкции на go:

С телефона блок кода не уставился

num, err := strconv.Atoi(str)
	if err != nil {
		fmt.Printf("Ошибка преобразования строки в число: %v\n", err)
		return
	} else {
	fmt.Printf("Успешно преобразовано: %d\n", num)
}

Можно даже сделать подобие через inline if

если мы знаем тип отлавливаемой ошибки

мы всегда его знаем - это Exception) Если серьезно, ситуации когда нужно в коде программы предпринимать какие-то действия по исправлению ошибки исчезающе редки, обычно либо нужно чтобы код не кидал ошибок вообще, либо плевать что он там кидает, всё равно чинить это потом ручками. То есть от эксепшена нужно две вещи:

  1. чтобы он содержал достаточно контекста для понимания произошедшего

  2. чтобы он не останавливал тот код, который не должен останавливаться

Никто не пишет на каждый чих по try-catch, они пишутся только там, где никакие ошибки не должны пролезть выше по стеку (ну или где хочется сделать жизнь юзера чуть проще и добавить больше контекста к произошедшему)

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

func reflectCall(fn reflect.Value, params []reflect.Value) (result []reflect.Value, err error) {
	defer func() {
		if r := recover(); r != nil {
			// Convert the recovered panic to an error
			switch x := r.(type) {
			case string:
				err = fmt.Errorf("%s", x)
			case error:
				err = fmt.Errorf("%w", x)
			default:
				err = fmt.Errorf("%v", r)
			}
			log.Printf("Panic occurred: %v\n", r)
			log.Printf("Stack trace:\n%s\n", debug.Stack())
		}
	}()
	return fn.Call(params), err
}

в итоге пользователь получает ошибку, а в лог пишется ошибка и стек трэйс.

почти как try catch ))

Я вот сейчас пальцем в небо тыкну и на первом же блоке с err != nil нам попадется

		if err != nil {
			return nil, err
		}

Проблема в том, что ошибки то не проверяются, а просто кидаются выше по дереву вызовов до места вызова. Из-за чего разработчики должны писать лишний бойлерплейт.
В Расте вот немного умнее подошли к обработке ошибок. Функция возвращает Enum, который может содержать ошибку или значение и ее можно распаковать оператором ?. Если имеем ошибку возвращаем ошибку, если нет, получаем значение.

fn check_even(num: i32) -> Result<i32, String> {
    if num % 2 == 0 {
        Ok(num)
    } else {
        Err(format!("Число {} нечетное", num))
    }
}

fn process_data(n: i32) -> Result<i32, String> {
    let val = check_even(n)?; // Распаковка через ?
    println!("Обработка значения: {}", val);
    Ok(val * 2)
}

fn main() {
    match process_data(4) {
        Ok(res) => println!("Результат: {}", res),
        Err(e) => println!("Ошибка: {}", e),
    }
    
    // Для нечетного числа
    match process_data(5) {
        Ok(res) => println!("Результат: {}", res),
        Err(e) => println!("Ошибка: {}", e),
    }
}

В Zig тоже достаточно похожая и органичная обработка ошибок, только там error union вместо Result и try вместо ? оператора.
Но авторы Го на любое предложение с мыслями о эволюции языка просто зыкрывают тему со словами "У нас все хорошо, ничего нам не надо". Я до сих пор не понимаю, каким чудом удалось в Го протолкнуть обобщения.

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

даже формат презентаций на GopherCon в этом году меняется. каждая презентация будет не более 30 минут и без времени на вопросы-ответы в конце. а всё потому что старикам уже в тягость всё это...

а всё потому что старикам уже в тягость всё это

Это либо неверно, либо дела совсем плохи - презентации делают одни старики.

в смысле? каждая конференция GoperCon построена на презентациях разработчиков. тем более всегда стараются привлечь для этого менее известных.

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

Нет там такой проблемы. Можете проверить и обработать. В верх ошибка подается чтобы в итоге ее где-то обработать и показать что-то пользователю. Никто не мешает обрабатывать ошибки там где вам надо.

Error.Is Error.As вам в помощь.

Тут скорее должен быть вопрос к написавшему: а почему бы ошибку не обработать на месте или не добавить ей контекста?

Исключения хороши тем, что вызывающий код может не заботится о проверке и обязательно не возвращать ошибку. Но на несколько уровней выше может быть обработка исключения. В go придётся вручную вести ошибку во всех функциях, это намного-намного менее удобней исключений.

В теории да, удобно, можно не обрабатывать не обязательные или маловероятные ошибки, меньше кода.

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

Скрытый текст

Человеческий фактор вообще неприятная штука. На рынке достаточно много разработчиков, которые не будут делать не обязательные вещи, даже если их об этом прямо попросить. Лично сталкивался с такими: иногда нужно 2-3 замечания, прежде чем до человека дойдет, что так писать не стоит, что это вызывает гнев у коллег. И есть некоторые уникумы, которые привыкли писать в своем стиле, и чужое мнение им вообще не аргумент, но таких к счастью совсем немного. И со всеми из них приходится, так или иначе, через их код, иметь дело. Особая когорта разработчиков, т.н. летуны: кода человек приходит на проект, пишет ужасный код, а потом увольняется, а ты через несколько лет огребаешь последствия за него, и тебе приходится все это переписывать по-человечески, потому что возникла потребность внести изменения именно в этот функционал. Почему такое вообще возможно? Потому что такие проекты фактически не имеют владельца, кодом никто не владеет, нет ни правил, ни надзора, заказчик понятия не имеет как управлять кодом и что там вообще есть, ему просто нужно исправить или изменить функционал готового кода, и он даже согласен если конкретно ты все приведешь в порядок, но бюджетов на значительные изменения в коде просто нет, и ты либо 80-90% работы сверх задачи делаешь бесплатно, либо делаешь только конкретно тот объем работ, который тебе оплатили.

Поэтому на практике это иногда вызывает боль.

Также исключения - это часть неявного поведения, магии. А магия тоже иногда выстреливает и причиняет боль: ищи потом, кто и где изменил поведение кода, где эта магия спрятана, ведь явных связей в коде для магии просто нет.

Думаю именно поэтому авторы go и заложили такие строгие ограничения в свое детище: просто устали от проделок менее разумных коллег, и устали от магии.

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

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

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

Меньше логики: проще писать, проще читать, проще понимать, проще дебажить, проще расширять (меньше - это разумеется без фанатизма).

Не понимаю про магию, можете детальней раскрыть мысль?

Ошибка в логе из исключения с полным трейсом. Везде скрипт, функция/метод и номер строки. Этого всегда достаточно, что бы 100% разобрать весь путь, понять логику и исправить. Намного проще чем кастомно это запихивать в одну ошибку по месту.

Я не очень много писал на go. Но всю дорогу так и не смог привыкнуть к обязательному пробросу ошибок. Как и не смог найти минус в отсутствии такой обязательной необходимости в PHP. Хотя 2 года писал параллельно на обоих.

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

Нет, никто не говорит, что структурный подход (с try/catch) единственно правильный.

Просто обычно в языках сильно больше возможностей работы с чем либо (н.р. разные типы циклов, разные возможности обработки ошибок и т.д.).

Ибо для решения разных проблем нужны разные подходы. Серебряной пули, какгрица, не существует.

f#, ocaml, rust

Result там возвращается.

да, но мы рассматриваем не средний индекс на рынке, а тихий сдвиг в особо крупных проектах

А откуда дровишки?
Можете своих циферок подкинуть подтверждающих этот сдвиг?

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

я поделился переводом только потому, что мне эта тема близка и интересна.

Автор оригинала - не американский разработчик, а индийский копирайтер который с нейросеткой "пишет" по 15 статей в день, а вы зачем-то это нейроговно несете на хабр.
Вот здесь этот клоун признается что он даже не программист
https://medium.com/@akshatkapil2249/this-one-chatgpt-prompt-made-me-5-000-no-coding-required-b03b08b7e387

какие 15 статей в день, когда у автора всего их около 15? ))
вы читали вообще другие статьи, чтобы делать вывод на основании одной статьи, написанной для "пиара"?

Вы ошиблись всего на порядок, у автора 118 статей. При этом он дорвался до llm-бредогенератора всего лишь 9 апреля, то ли еще будет! Другие статьи "автора" (как и эту) я, конечно же, не читал, у меня еще с середины 2023 года выработался иммунитет к medium и нейроиндусам, которые заполонили эту площадку и окончательно испортили.

не буду спорить насчет того, что сейчас много кто генерит контент. но данная статья прочитана мной от начала до конца и переведена почти дословно только потому что я именно в этой сфере сам работаю и вижу тенденцию, которая вполне правильно описана в этой статье.

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

я проверил и оказалось 116 - это 117 минус первая (последняя в списке) пустышка

Это прям в корне всё изменило (нет).

В особо крупных проектах Go и так не использовался часто

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

cockroachdb для вас достаточно крупный проект? kubernetes? grafana? terraform?

Tiobe index апрель 2025

  1. Visual Basic +1.24%

  2. Delphi/Object Pascal +1.06%

  3. Fortran +0.57%

очень, очень хороший источник

Пробежал текст статьи "по диагонали" (это важно) - и вижу тут не столько про Go, сколько НЛП-шку для программистов на тему "Rust лучше по-любому" ))

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

Взгляд со стороны..

Go: мы написали на го Docker, Terraform, Kubernetes

Rust: вот вам на расте новый cat, новый grep, ls и новый top с цветами и колокольчиками!

да, оно-то так и есть если посмотреть со стороны. но со стороны не всем видно, что происходит в Big Tech, о котором идет речь.

Так а примеры то будут? Я вижу что это перевод, но вы его так активно защищаете что хочется попросить примеры.

Любая большая вещь, превращаясь в индустрию, начинает жить своей жизнью. Уже неважно для чего она была - важна жизнь внутри.

Джуны растут до синьоров, внедряются внедрения, идёт процесс ради процесса и бег ради бега.

Придумаем 100500 новых языков с перламутровыми пуговицами, перепишем старое, потом перепишем ещё раз, потому что у самурая нет цели, есть только Путь.

Пока есть те, кто готов инвестировать в ожидание выгоды.

Просто разница подходов -- пока одни скачут по верхам (тоже полезным), другие пишут фундамент.

ну почему, есть и аналог elasticsearch и другие вещи, например неделю назад в новостях был arroyo, как я понимаю это аналог apache flink. Вообще-то есть еще и firefox с которого я прямо сейчас пишу. Не адвокат rust, более того меня больше напрягают эти приписки "сделано на расте" потому что идет какое-то навязывание, что после этого софт почему-то сразу будет работать со сверхсветовой скоростью. Я думаю надо дать еще время, код на расте ж вроде пишется не так быстро как на go или java :)

Какой-то пиар Rust.

может надо добавить подзаголовок "Это не пиар Rust"? ))

Лучше ключевые слова такие добавить )))

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

Заключительные мысли: Go не умер — но его время на вершине может закончиться

Не в упрёк или попытаться что-то оспорить, просто ради интереса: а что это за вершина?

Все бегут на раст: Это же С++, но лучше!!!

А, потом все побегут на аналог PHP: На нем быстрее чем на расте!

А, потом...

аналог PHP

Дык это ж node.js, и на него все уже убежали. Но таки да, не быстрее раста))

Мне кажется PHP менее требователен к ресурсам, чем нода.

Когда "и удобный, и быстрый, и безопасный", всегда есть "но". Иначе это прям идеальный язык получается.

Так я не понял, Big Tech уходит от Go, или это домыслы автора исходя из минусов языка?

Зачем переводить статью какого-то индуса, который холиварит на тему "Rust круто, давайте всё на Rust перепишем, а язык {подставьте любой} уже устарел, его на помойку". На Rust-е за 12 лет существования ржавого на нем написаны буквально единицы крупных проектов, типа Fuchsia OS и часть Firefox(может ещё есть удачные примеры, но их надо специально выискивать), когда как на любом другом по 5-10 пишут каждый код.

Rust идеально подходит как нишевый язык, на котором можно писать отдельные компоненты/плагины/сервисы, где критична не скорость( по ней он тем же плюсам проигрывает), а безопасная работа с памятью. А всё остальное можно писать на чем-то нормальном, что знает больше людей при одинаковом уровне удобства и не морочить голову с поддержкой, поиском и заменой кадров, обучением новичков и т.д.

Даже в подборке работы от Хабра очевидно, какой перекос и что если за десяток лет он не исправился - не исправится и дальше

Надо учесть, что русскоязычный Big Tech и западный отличаются довольно сильно. Если там идет тихий переход с одного к другому, то это не значит, что тут это станет сразу заметно и ощутимо.

Если в Cloudflare или Dropbox переписали внутренние платформы на Rust, то это вообще никак не отразится на рынке.

А тут речь идет именно об изменениях внутри крупных компаний.

Мне кажется с языками программирования все немного сложнее чем описано в статье: не может просто большая компания взять, отказаться от одного языка (на котором написано куча отлаженного бэкэнд софта) и начать внедрять другой язык. Нужно чтобы либо сотрудники компании освоили новый стек - либо где-то взять новых сотрудников на новый язык.
Go на мой взгляд - изначально был инструментом оптимизации, который не имеет ООП, но зато удобнее в работе с многопоточностью чем Си.
И если что-то уже оптимизировано на Go - то зачем это вдруг переделывать на Rust или Kotlin?
Я чуть чуть работал с Go и с Rust - и конечно Rust ощущается удобнее и веселее в разработке - но мне кажется странным, что если биг тех освоил Go - он возьмет и сразу будет переделывать это на Rust? Как будто в мире полно рыбы - и если что-то нужно оптимизировать - то это действительно какой-нибудь старый бэкенд на интерпретируемом языке (или даже бывает новый бэкенд на питоне пишут, потом переписывают на что-то побыстрее) - и тут любые быстрые языки хорошо - и компании будут выбирать исходя из возможностей своих специалистов.
Или я не правильно понимаю как дела делаются в бигтехе?

Go когда-то доминировал в инструментах для серверной части, CLI и DevOps.

А когда это было? Я все проспал?

Docker, Terraform, Prometheus, Grafana и так далее

Статья написана в вакууме.

Например, по поводу ошибок. В наше время даже самый тупенький ИИ дополняет if err != nil раньше, чем вы подумаете о том, что вам надо обработать ошибку.

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

if err != nil гораздо проще для мозга, чем дополнительные сущности, как в rust, zig и (плюёт три раза через плечо) Java. В результате понимание простого Go кода происходит быстрее. Именно тут и появляется экономия времени. Эти минус доли секунд восприятия в конце рабочего дня складываются в меньшую усталость и большее удовольствие от работы.

Typescript и Rust крайне легко позволяют программисту заниматься "сублимацией" над типами и прочими хитрыми конструкциями языка. В результате никто не понимает, как этот код на самом деле работает. Даже его писатель забудет об этом. Go просто не даёт возможности написать переусложненный код. И в результате его рефакторинг гораздо проще даже для новых людей.

То, что крупные корпорации увеличивают использование Rust говорит только о том, что им в определенных местах нужно то, чего Go не даёт: это как раз наличие сборщика мусора, который, к сожалению, даёт непредсказуемые задержки, и, конечно же, Rust более производителен в супер-интенсивных частях инфраструктуры. Раньше они эти части тупо писали на C.

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

Да, Golang прост для чтения и понимания. Но это не мешает мне считать не самым удачным решением бесконечные err != nil в коде. Но да, это просто для чтения, спору нет.

И да, Rust сложен для чтения. Не понимаю, причем тут TypeScript - это C#/Java только чуть сбоку. Мне вообще показалось, что TypeScript упоминается как средство запуска WebAssembly-модуля, написанного на Rust )

В контексте TypeScript скорее NodeJS есть за что критиковать - все крайне быстро развивается и устаревает, зачастую статьи полу-годовой давности уже не работают как надо.

Go просто не даёт возможности написать переусложненный код.

В Go есть if, else, for, nil, указатели, есть даже рефлексия. Что вообще мешает с помощью этого написать код любой переусложненности? Или по вашей логике на C просто невозможно написать переусложненный код? Или если взять Java 5 версии где в основном были if да for, то там весь код был куда комфортное и читабельнее а разработчики продуктивнее? Не хочу расстраивает но я видел проекты на java 5.

то, чего Go не даёт: это как раз наличие сборщика мусора, который, к сожалению, даёт непредсказуемые задержки

А в чем проблема не использовать сборщик мусора на Go когда это требуется?

Язык активно "мешает" писать сложный код. Он "сопротивляется" этому. А так как хороший программист должен быть ленивым (больше думать, чем плевать кодом), то, используя Go, мы код меньше переусложняем.

Если отключить в Go сборщик мусора, то уже лучше на C писать. Головная боль с памятью будет такой же, но и скорость работы будет выше. В этом конкретном случае Rust - идеальное решение.

  1. Go был придуман Google с целью УДЕШЕВЛЕНИЯ разработки. Чтобы можно было писать простой код усилиями тысяч индусов

  2. Go был положительно встречен всеми потому что в момент его появления писать кровавый энтерпрайз на линуксе банально было нечем. Разработка на C/C++ идет слишком медленно, а джава была монстрообразна, а C# жил на windows платформе

  3. В Россию Go пришел как дань моды и у нас не получилось так что Go удешевляет разработку, наоборот, где требуется 1-2 Java/C# разработчика разработчиков Go для аналогичной системы нужно в 1.5-3 раза больше.

  4. Сейчас есть платформонезависимые экосистемы Rust, Java+Kotlin, C#/.NET каждая из которых стала лучше Go, но Go продолжает по инерции использоваться во многих проектах

Что ожидаю лично я. Поскольку Go изначально создавался с целью удешевления разработки то на нем будут писать ИИ-агенты и LLM. Он максимально простой и идеально впишется в новый мир разработки с применением ИИ. Что-то не нравится - ИИ перепишет говнокод на Go на другой говнокод который лучше и никакие индусы не нужны

Тонна слов и мало дел. Давайте соревнование устроим. Кидайте задания сюда и решения на го/раст и сравним что лучше

Короче говоря, можно уже не переходить с php на go? А то я не успел)

Вот это отличная статья, куча статистики! Спасибо!

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации