Пользователь
121,5
рейтинг
1 ноября 2015 в 02:42

Разработка → Ошибки — это значения перевод

Go*
(Перевод статьи из официального блога Go)

Частой темой среди Go программистов, особенно тех, которые только познакомились с языком, является вопрос о том, как обрабатывать ошибки. Разговор часто сводится к жалобам на то, что последовательность
if err != nil {
    return err
}

появляется слишком часто. Недавно мы просканировали все open-source проекты, которые мы только смогли найти и увидели, что этот сниппет появляется лишь раз на страницу или две, гораздо реже, чем многие могли бы подумать. И всё же, если впечатление того, что вы должны всегда писать
 if err != nil
остается, значит, очевидно, что-то тут не так, и мишенью оказывается сам Go.

Это неверно, это вводит в заблуждение и это легко исправить. Наверное происходит следующее — программист, знакомясь с Go, задаёт вопрос — «Как я должен обрабатывать ошибки?», заучивает этот паттерн и тут и останавливается. В других языках, это может быть блок try-catch или другой механизм обработки ошибок. Соответственно, думает программист, там где я бы использовал try-catch в моём старом языке, в Go я просто напечатаю if err != nil. Со временем, в Go коде накапливается много таких сниппетов, и результат выглядит неуклюже.

Но вне зависимости от того, как это объясняется на самом деле, очевиден тот факт, что эти Go программисты упускают фундаментальную идею ошибок: Ошибки это значения.

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

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

Вот простой пример из пакета bufio, тип Scanner. В нём метод Scan осуществляет низлежащие операции ввода-вывода, которые, разумеется, могут вернуть ошибку. Но при этом, метод Scan, не возвращает ошибку вообще. Взамен, он возвращает boolean, и отдельный метод, который может быть запущен в конце процесса сканирования, сообщая, произошла ли ошибка. Пользовательский код выглядит примерно так:
scanner := bufio.NewScanner(input)
for scanner.Scan() {
    token := scanner.Text()
    // process token
}
if err := scanner.Err(); err != nil {
    // process the error
}

Безусловно, тут есть проверка ошибки на nil, но она происходит и выполняется лишь раз. При этом, метод Scan мог бы быть определён как
func (s *Scanner) Scan() (token []byte, error)

и пользовательский код мог выглядеть как-нибудь так (в зависимости от того, как извлекается токен),
scanner := bufio.NewScanner(input)
for {
    token, err := scanner.Scan()
    if err != nil {
        return err // or maybe break
    }
    // process token
}

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

Под капотом всего этого, конечно, происходит следующее — как только Scan обнаруживает ошибку ввода-вывода, она сохраняет ошибку и возвращает false. Отдельный метод, Err, её возвращает, когда клиент запросит. Тривиально, но это не тоже самое, что вставлять
if err != nil

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

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

Тема повторяющегося кода для обработки ошибок поднялась, когда я был на конференции GoCon осенью 2014 в Токио. Гофер-энтузиаст, под ником @jxck_ в Твиттере, повторил частую жалобу про проверку ошибок. У него был некоторый код, который схематически выглядит примерно так:
_, err = fd.Write(p0[a:b])
if err != nil {
    return err
}
_, err = fd.Write(p1[c:d])
if err != nil {
    return err
}
_, err = fd.Write(p2[e:f])
if err != nil {
    return err
}
// and so on

Это слишком повторяющийся код. В реальном примере, который был длиннее, было ещё масса кода и было непросто просто так отрефакторить этот код, создав функцию-хелпер, но в общем случае, литерал функции, замыкающую в себе обработку ошибки мог бы помочь:
var err error
write := func(buf []byte) {
    if err != nil {
        return
    }
    _, err = w.Write(buf)
}
write(p0[a:b])
write(p1[c:d])
write(p2[e:f])
// and so on
if err != nil {
    return err
}

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

Мы можем сделать этот код более ясным, более общим и переиспользуемым, если позаимствуем идею из метода Scan, которую обсуждали выше. Я упомянул эту технику в нашем разговоре, но @jcxk_ не понял, как её применить. После длинной беседы, несколько стесненной языковым барьером, я спросил, могу ли я просто взять его ноутбук и показать пример, написав реальный код.

Я определил объект под названием errWriter, примерно вот так:
type errWriter struct {
    w   io.Writer
    err error
}

и дал ему один метод, write. Он не должен даже следовать стандартной Write сигнатуре, и он намеренно с маленькой буквы, чтобы подчеркнуть отличие. Метод write вызывает Write метод низлежащего Writer-а и сохраняет ошибку для использования в будущем:
func (ew *errWriter) write(buf []byte) {
    if ew.err != nil {
        return
    }
    _, ew.err = ew.w.Write(buf)
}

Как только ошибка произойдёт, метод write станет no-op, но значение ошибки будет сохранено.

Имея тип errWriter и его метод write, мы можем переписать код выше:
ew := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
// and so on
if ew.err != nil {
    return ew.err
}

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

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

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

По факту, этот паттерн появляется в стандартной библиотеке весьма часто. Пакеты archive/zip и net/http его используют. Более релевантный к этой дискуссии, Writer в пакете bufio является, по сути, реализацией идеи errWriter. Хотя bufio.Writer.Write возвращает ошибку, это, в основном, для имплементации интерфейса io.Writer. Метод Write в bufio.Writer ведёт себя также, как и errWriter.write в примере выше, и вызывает Flush в момент возврата ошибки, так что наш пример мог бы был записан следующим образом:
b := bufio.NewWriter(fd)
b.Write(p0[a:b])
b.Write(p1[c:d])
b.Write(p2[e:f])
// and so on
if b.Flush() != nil {
    return b.Flush()
}

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

Мы посмотрели лишь на одну из техник избегания повторяющегося кода проверки ошибок. Имейте ввиду, что использование errWriter или bufio.Writer — не единственный способ упростить обработку ошибок, и этот подход не подходит ко всем случаям. Ключевой урок тут в том, что ошибки — это значения, и вся мощь Go как языка программирования в вашем распоряжении чтобы их обрабатывать.

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

Но помните: Чтобы вы ни делали, всегда проверяйте ошибки!

И в заключение, для полноты картины моего общения с @jxck_, включая небольшое записанное им видео, посмотрите его блог.
Перевод: Rob Pike
divan0 @divan0
карма
128,0
рейтинг 121,5
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (136)

  • +23
    scanner := bufio.NewScanner(input)
    for scanner.Scan() {
        token := scanner.Text()
        // process token
    }
    if err := scanner.Err(); err != nil {
        // process the error
    }
    


    Какой механизм не дает программисту забыть вызвать проверку на scanner.Err()?

    Вообще, конечно, чем больше я вижу проверок на ошибки в Go-коде, тем больше меня удивляет тот факт, что при, казалось бы, очевидном родстве с монадой Try, ее все-таки решили проигнорировать.
    • –19
      Какой механизм не дает программисту забыть вызвать проверку на scanner.Err()?

      Документация. Это же библиотека. а не конструкция языка.
      • +17
        Документация. Это же библиотека. а не конструкция языка.

        И это считается хорошим дизайном?
        • –12
          И это считается хорошим дизайном?

          Считается ли документация хорошим дизайном библиотеки?
          Да, рекомендую попробовать писать документацию.
          В Go, кстати, для этого достаточно просто написать комментарий над функцией, начинающийся с имени функции — и готовая удобная HTML-страничка готова или доступна через godoc.org.
          • +16
            Нет, считается ли хорошим дизайном вынос информации об ошибке в отдельный метод (в данном случае — Err), вместо ее возврата сразу в методе, где она возникла?
            • –8
              Статья именно об этом. Вы её не прочли или не поняли?
              • +9
                У меня из статьи создалось ощущение, что да, это считается хорошим дизайном. Я все правильно понял?
                • –7
                  Нет, вы совсем неправильно поняли.
                  • +16
                    Эм, а как тогда понимать код в статье? Мне говорят, что надо не сразу получать ошибку и ее обрабатывать, а пробежаться все сотни и миллионы итераций с no-op, чтобы потом в конце узнать, что оказывается на первой итерации была ошибка и мой цикл в пустую делал работу. Это херота какая-то и ужасный просто совет. Я удивлен, что такое советуют в официальном блоге. Ошибка должны быть обработана в том месте, где она произошла. Если код из-за этого становится страшным, то, ну не знаю. Вроде бы давно ясно, что обработка ошибок многословна. Либо надо таки как-то рефакторить код. Но не делать костыль как в статье.

                    Каким образом совет в статье связан с «программирование с использованием значений ошибок» решительно не ясно.
                    • –14
                      Ну вы хоть прочитали, и не поняли, это простительно.

                      Мне говорят, что надо не сразу получать ошибку и ее обрабатывать, а пробежаться все сотни и миллионы итераций с no-op

                      Вам так не говорят. В статье рассматривается конкретный пример.

                      Это херота какая-то и ужасный просто совет

                      Это ужасный комментарий, особенно если вы не поняли статью.
                      • +7
                        Вам так не говорят. В статье рассматривается конкретный пример.

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

                        Мне прямым текстом говорят, что один вариант плохой, а другой хороший. Так что, еще раз, как иначе понимать статью, если не так? Вы уже который комментарий ходите вокруг да около, кидаете какие-то намеки, но так конкретно и не говорите, как же надо понимать то, что написано черным по белому в статье.
                        • –11
                          а пробежаться все сотни и миллионы итераций с no-op

                          Ещё раз — в статье рассматривается конкретный пример.
                          Покажите мне в этом конкретном примере «сотни и миллионы», пожалуйста.

                          Мне прямым текстом говорят, что один вариант плохой, а другой хороший

                          Покажите «прямой текст» где вам говорят «один вариант плохой, другой хороший».
                          • +9
                            Так кто же не читал собственную статью

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

                            Вот здесь мне говорят, что один вариант лучше другого.

                            Под капотом всего этого, конечно, происходит следующее — как только Scan обнаруживает ошибку ввода-вывода, она сохраняет ошибку и возвращает false. Отдельный метод, Err, её возвращает, когда клиент запросит

                            Вот здесь мне говорят, что лучше пробежаться потенциально сотни и миллионы итераций с no-op
                            • 0
                              1. «Выглядит более естественным» != «лучше»

                              2. Предлагаемый паттерн (он широко используется и в других языках, в частности в С, а также везде, где C-библиотеки импортируются с минимальными обертками) просто разделяет индикацию факта ошибки и получение её описания, переводя последующие вызовы функций в режим no-op до сброса флага ошибки (явного, путем вызова соответсвующий функции или даже прямого сброса флага, или неявного, путем получения описания ошибки). Разработчик клиента сам решает анализировать ему индикатор (возврат false) на каждой итерации или попытаться сначала выполнить все, а лишь потом узнать была ли ошибка, в зависимости от функциональных и нефункциональных требований к клиенту.
                              • +6
                                он широко используется и в других языках, в частности в С

                                Чтобы было по-сишному, ф-ция Scan должна возвращать не boolean, а одно из {DATA_FETCHED, EOF, ERROR}.
                                В этом случае минимальный цикл оставался бы таким же простым (while Scanner.Scan()==DATA_FETCHED), но из интерфейса было бы кристально понятно, что может быть как ERROR, так и EOF, не надо смешивать эти ситуации
                                • 0
                                  Scanner скрывает EOF, поэтому ваш интерфейс скукоживается до {DATA_FETCHED, ERROR}, что суть есть bool.
                                  оригинальная претензия все равно про то что проверка будет после цикла, возвращать bool быстрее чем interface{} видимо дело в этом
                                  • 0
                                    У Scanner плохо то, что даже если я не хочу разбираться c ошибкой и мне достаточно факта её наличия, в любом случае нужен лишний в данном сценарии вызов Err().
                              • +4
                                Проблема в том, что этот паттерн был придуман на заре программирования, когда никакой многопоточности и в помине не было. И сейчас следовать ему — создавать огромную головную боль тем, кто решит использовать этот «чудесный» API в многопоточном окружении. И с чего это автор статьи решил,
                                Но зачастую, подход всё-или-ничего в конце бывает достаточным.

                                ? Такое допустимо только в скриптиках, которые написал, выполнил и забыл. Но при таком программировании вообще много чего допустимо, чего в поддерживаемом коде никогда не напишешь.
                                • –1
                                  С многопоточностью нет никаких проблем, потому что глобальная переменная errno, содержащая последний код ошибки, объявляется со спецификатором __thread. Операционная система помещает эти переменные на страницу памяти, в которую при переключении потока мапится физическая страница с переменными конкретного потока, каждый поток имеет свою версию errno.

                                  Другое дело — уровень детализации ошибки. С исключениями приятно иметь дело: у каждого исключения есть место возникновения, произвольный контекст и цепочка InnerException. Пришёл AuthorizationException, в котором есть UserName и Password, а внутри DatabaseException, в котором SQLCommandException, в котором указана команда insert into LOGIN_LOG, а внутри ещё исключение — OutOfDiskSpaceException.

                                  Всё сразу ясно, но используется это только программистом для диагностики, а когда всё хорошо работает, это лишний код. Возможно, для всяких high load и big data маятник качнулся в обратную сторону — минимум оверхеда на диагностику, минимум информации об ошибках, максимум скорости. Возможно, Go был написан с этими соображениями.
                    • +3
                      Для объективности — миллионов итераций с no-op не будет, Scanner.Scan «returns false when the scan stops, either by reaching the end of the input or an error.» и ошибка будет обработана, если код написан в расчете именно на эту логику — а так как нам нужно ее обработать, мы этот код напишем.
                  • +3
                    То есть, это не считается хорошим дизайном, не надо так делать?
                    • –8
                      Да что ж такое.
                      Статья о другом, у нее абсолютно другая тема, другой посыл и другой вывод.
                      А вы, похоже, все еще пишете комментарии к другой статье, про «хороший/плохой дизайн».
                      • +9
                        Так просто ответьте на вопрос — пример в статье это хороший дизайн или плохой?
                      • +8
                        Ну так напишите какая у статьи тема, чём посыл и какой сделан вывод. Ибо, судя по вашим словам, люди в основном с пониманием этих вещей ошибаются.
                        • –6
                          Ну так напишите какая у статьи тема, чём посыл и какой сделан вывод.

                          Вы хотите, чтобы я сначала переводил статьи, а потом их пересказывал? Я вас правильно понял?
                          • +9
                            Я хочу, чтобы вы не только говорили, что комментатор ошибается, но и указвали в чём ошибка и как правильно.
                            • –10
                              Я и указал. Кроме того, это не комментатор, это lair, у него 1 пост и 8 тысяч комментариев. Он не пишет на Go, но ходит во все посты и рассказывает, какой Go плохой. Вам интересно с такими людьми общаться?
                              • +3
                                Ответьте мне тогда, я не lair если что. Очень хочу понять как надо делать правильно, но ваш комментарий поставил меня в тупик.
                      • +5
                        Статья о другом, у нее абсолютно другая тема, другой посыл и другой вывод.

                        Я, вроде, задал простой вопрос к одному из примеров в статье.
                        • +2
                          практически все парсеры в Go от google возвращают ошибки именно так, то есть когда у вас есть функция
                          Parse(text string) (*Template, error)
                          и она сразу может вернуть результат, то с результатом должна идти и ошибка,
                          когда создается объект парсера у него есть метод Err (например https://godoc.org/golang.org/x/net/html)
                          По сути если что-то вызывает 1 раз, то там вернут сразу err, а если что-то вызывается 100500 раз, там будет один результат умещающийся в регистр процессора, видимо логика ещё и в этом.
                          • +2
                            Ну то есть это все-таки рекомендуемый дизайн?
                            • –1
                              ну да, оффициальное объяснение есть в статье:
                              > В существующем же дизайне API, пользовательский код таким образом выглядит более естественным: сначала пройтись до конца по токенам, а потом проверять и волноваться об ошибках. Обработка ошибок не прячет поток управления.
                              • +2
                                divan0 при этом написал наоборот. Обидно.

                                Меня в таком дизайне расстраивает тот уже упомянутый факт, что он противоречит ранее обещанному принципу «ошибки явны и их можно проигнорировать только специально». Здесь достаточно забыть вызвать Err — и никогда никогда об ошибке не узнает.

                                (ну и униформность нарушается тоже, но это уже второго порядка)
                                • 0
                                  > ошибки явны и их можно проигнорировать только специально
                                  как обычно есть одно «но», когда вы проигнорировали вообще весь результат функции
                                  > Здесь достаточно забыть вызвать Err — и никогда никогда об ошибке не узнает.
                                  если никто не узнает то и проблемы считай нет.
                                  А если серьезно, то формально вы вроде правы.
                                  Но реально стандартные токенизаторы все так написаны это де факто уже соглашение, они конечно путаются, что делать с io.EOF, но вот .Err() уже считай стандарт. это во многом уходит во внутренности парсинга на Go, где проще в структуре ошибку сохранить чем в каждую функцию в результате возвращать.
                                  Ну и в доке сразу правильный пример идет как ошибку обработать.

                                  И декларация кстати в том, что ошибка идет вместе с результатом, чтобы вы их не разделяли. и тут получается что результат у нас токен, а он лежит внутри структуры парсера, там же где ошибка иии формально все классно…
                                  ну, а так у авторов точно пунктик по поводу скорости, я думаю возврат условно byte вместо, byte + {pointer + pointer } (1 + 8 + 8) тут тоже свое сыграл.
                                  • +5
                                    как обычно есть одно «но», когда вы проигнорировали вообще весь результат функции

                                    Да в том-то и дело, что нет.

                                    scanner := bufio.NewScanner(input)
                                    for scanner.Scan() {
                                        token := scanner.Text()
                                        // process token
                                    }
                                    


                                    Я где-то проигнорировал весь результат функции?

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

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

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

                                    Мне не шашечки, мне ехать. А когда ехать, выясняется, что забыл программист вызов — и получили мы внезапно проигнорированную ошибку при чтении файла, и, как следствие, тихую потерю данных.
                                    • –2
                                      > Да в том-то и дело, что нет.
                                      да я про сам принцип писал, что он не 100% верен.

                                      то есть
                                      scanner := bufio.NewScanner(input)
                                      for scanner.Scan() {
                                      token := scanner.Text()
                                      // process token
                                      }
                                      само появилось из воздуха, а то что там Err есть вы не догадались? То есть, нашли пакет, конструктор, у типа нашли метод, нужный, поняли что его надо в условие цикла поставить, и вызывать Token у сканера, не прочитав ни доки, ни описания к методам ни их кода. И с такой проницательностью не поняли что там есть Err?!
                                      Вы понимаете, реально как человеку который пишет на Go мне слух режут все эти разговоры, о 100500 мега ппц важных проблем которых для меня как человека который почти каждый день на нем пишет на самом деле нет.
                                      А из более менее реального кроме отсутствия дженериков (ну попробуй не заметь) никто ничего назвать не может.
                                      • +6
                                        То есть, нашли пакет, конструктор, у типа нашли метод, нужный, поняли что его надо в условие цикла поставить, и вызывать Token у сканера, не прочитав ни доки, ни описания к методам ни их кода. И с такой проницательностью не поняли что там есть Err?!

                                        Ну да, типичное программирование по подсказкам IDE.

                                        Дело же не в том, что не понял, а в том, что однажды можно задуматься, отвлечься и забыть вписать.

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

                                        Я не говорю, что это мегаважная проблема. Я говорю, что это — плохой дизайн (точнее, дизайн хуже, чем можно было бы). С ним можно жить, с ним можно писать хорошие системы, но он сам от этого лучше не станет.
                                        • +1
                                          > Ну да, типичное программирование по подсказкам IDE.
                                          Ну ок, только: https://hsto.org/files/b95/67e/a22/b9567ea224c64ab7bed46d4d67cb1ffd.png

                                          Какой бы ни был дизайн, все мы люди и задумался забыл и т.п. от языка не очень зависит.
                                          Вот чуть расширенный подход, где возвращается тип токена, а не bool https://godoc.org/golang.org/x/net/html
                                          так лучше, или все равно плохо?
                                          • +1
                                            Вот чуть расширенный подход, где возвращается тип токена, а не bool godoc.org/golang.org/x/net/html
                                            так лучше, или все равно плохо?

                                            Нет. Более того, становится все хуже, потому что для языка, в котором количество избыточных сущностей минимизировано, четыре способа сообщить об ошибке — это многовато.
                                            • +1
                                              Это не 4 типа сообщать об ошибках, т.к. вы Err() посчитали дважды (или для вас bool в Scan и TokenType в Next() это 2 разных способа сообщать об ошибках?!) и токенайзеры на что-то существенное не бывают без типов токенов (а там где бывает там bool), при чем EofToken это стандартная практика их написания, чтобы делать процесс обработки проще. А т.к. в Go ошибки это значения и io.EOF это error то он становится ErrorToken.
                                              Как думаете что основное в них, разобрать входной поток на токены/лексемы или вернуть ошибку? Может у вас есть пример как сделать дизайн токинизаторов (лексических парсеров) лучше?
                                              • +4
                                                Это не 4 типа сообщать об ошибках, т.к. вы Err() посчитали дважды (или для вас bool в Scan и TokenType в Next() это 2 разных способа сообщать об ошибках?!)

                                                Да, два разных. Результат возврата Scan сообщает не об ошибке, а о прекращении прохода (а уж причину будь ласка узнавать из Err).

                                                Может у вас есть пример как сделать дизайн токинизаторов (лексических парсеров) лучше?

                                                Дело не в токенайзерах, дело в сквозном для языка подходе — которого здесь нет. Reader.ReadString возвращает (string, error), но Scanner.Scan возвращает bool, а за ошибкой идите в другую сторону. Хотя и то, и другое можно использовать для построчного чтения файла.
                                    • 0
                                      прошу прощения что вмешиваюсь в Вашу беседу
                                      у меня лично к Вам lair есть вопрос

                                      посоветуйте пожалуйста язык программирования в котором:
                                      • можно сразу писать безопасный код не смотрев доку
                                      • в котором идеальный дизайн языка


                                      не троллинга ради мне реально интересно
                                      • 0
                                        Я такого не знаю.
            • 0
              В методе возвращается индикатор факта наличия ошибки, в отдельном методе — её описание. В языках, где нет исключений (или есть с некоторых пор, но до сих пор испытывающих сильное влияние времён, когда не было) — это обычная практика. Как правило лучшая, чем проверка даже факта наличия ошибки вызовом отдельного метода.
              • +7
                Неа. В описанном примере Scan может вернуть false (т.е., по вашему мнению, индикатор ошибки) даже в том случае, когда Err вернет nil (т.е., ошибки не было) — это случится в конце потока. Поэтому индикатором именно наличия ошибки служит сравнение Err с nil

                Более того, как раз в обсуждаемом языке стандартный шаблон возврата ошибок предполагает одновременный возврат результата и ошибки в кортеже:
                f, err := os.Open("filename.ext")
                if err != nil {
                    log.Fatal(err)
                }
                


                (… точнее, это я думал, что он стандартный. Теперь уже и не знаю, что думать.)
      • +10
        Я просто процитирую вашу же статью: «И эта простота и явная невозможность проигнорировать ошибку создает стимул.»

        Я думал, что невозможность проигнорировать заложена в язык и навязываемые хорошими практиками шаблоны. Но нет, мы только что увидели пример кода от авторов Go (bufio же из стандартной библиотеки, я ничего не путаю?), где ошибку легко можно проигноровать.
        • –4
          Её не легко проигнорировать — это библиотека, которой прежде, чем пользоваться, нужно открыть документацию. Я не знаю, зачем вы тратите столько сил, чтобы выдать желаемое за действительное.
          • +5
            Не открыть документацию/не прочитать документацию/неправильно понять документацию — легко. Для того, чтобы проигнорировать ошибку, не надо ничего делать специально — достаточно не сделать обращения к методу.
            • –11
              Спасибо за ценные теоретические рассуждения о том, как на самом деле обстоят дела с библиотеками и документацией Go. Вам, безусловно, виднее, вы же на Go не пишете.
              • +9
                А что, люди, пишущие на Go, читают документацию как-то иначе, нежели это делают все остальные люди?
                • +3
                  Они как минимум обращают внимание на обработку ошибок от этой самой либы.
                  Мне приходилось писать на c#, java, c++, python и ruby и только перейдя на Go я заметил, что здесь люди готовы обрабатывать ошибки, а не просто игнорить до того момента пока оно само не грохнется.
                  • 0
                    Это какое-то универсальное правило или я такой уникальный, что в шарпах ошибки и исключения ловлю? Смотрю в документации, как функция может завалиться.
                    Зато я вот заметил другое — в Go людям плевать на ошибки. И мне здесь уже советовали плевать на ошибки. Все равно их все не обработать.
                    • +1
                      Зато я вот заметил другое — в Go людям плевать на ошибки.
                      Вам целую статью написали, что не плевать.
                      И мне здесь уже советовали плевать на ошибки. Все равно их все не обработать.
                      Это просто неправда, либо люди не компетентны либо вы неправильно их поняли, исказив смысл под своё мнение. Вот цитата из документации к bufio/scanner:
                      * Scan advances the Scanner to the next token. It returns false when the scan stops, either by reaching the end of the input or an error.
                      * Err returns the first non-EOF error that was encountered by the Scanner.

                      То есть, на первой же не-EOF ошибке сканер останавливается, никто не говорит, что он всё игнорирует. А ошибка с которой он вышел доступна через метод Err() это уже ваше дело, что с ней делать. Авторы языка и автор статьи призывают её обработать.
                    • +1
                      не надо врать, речь была о типизации ошибок, а не о факте их возникновения
                      • 0
                        Я прекрасно помню, о чем была речь. Отсутствие проверок на тип произошедшей ошибки = отсутствие обработки ошибок. Выплюнуть nil и сообщение в консоль это игнорирование ошибки больше.

                        Как еще понимать вот это
                        я вижу метод с ошибкой и сразу пишу, что мне делать, что там за ошибка мне в принципе не очень то и важно.
                        • 0
                          Если я делаю reconnect при обрыве сокета, то 99.9% мне всеравно почему это произошло, закрыл ли это соединение внешний сервер, произошел ли таймаут, ОС решила грохнуть и т.п. я просто среагирую на ошибку и сделаю переподключение.
                          Если нужна доп. инфа, что именно произошло, то я вам привел ВСЕ места в стандартной либе где есть кастомный тип ошибки или интерфейс и указал, что это не сильно то и поможет. потому что если вы хотите отличить то, что порт занят другим процессом, от того что у пользователя прав не хватило забиндится на 0.0.0.0 (честно — не знаю зачем), то это только текст ошибки парсить. а так лучше не делать.
                          • +5
                            Вы читаете вообще. что вам пишут? Почему вы думаете, что у всех такой примитивный use case, когда можно просто грохнуться и ругнуться юзеру в консоль? Есть конкретные случаи, когда нет юзера, приложение автономно, ему некому и некуда бить тревогу. Оно должно всеми силами восстановить соединение. Простой пример, у меня были задачи, когда биндинг происходит на конкретный адаптер. Если этого адаптера нет, то в зависимости от настроек запускаются разные алгоритмы восстановления соединения, включая простую долбежку до победного, но это лишь один из вариантов. И в этом случае ошибка мне много чего скажет о том, как мне поступить. Потому что нет юзера, нет лога ошибок, есть требование — приложение должно само восстановить соединение и ни в коем случае не упасть из-за безобидной ошибки биндинга.

                            И я еще раз повторю, раз вы пропустили это. Listen это один из примеров. ВСЯ документация написана в ключе — ошибка возвращается, а какая — не скажу. Не счесть примеров, когда ошибка нужна и от нее зависит поведение программы. Я понял, что надо лезть в исходник и, за неимением альтернатив, смирился с этим. Но это признак плохой и, попросту, отсутствующей документации. Ее просто нет. Есть список функций на сайте. Чем-то напоминает doxygen документацию у многих инструментов — такая же бедная и бесполезная. Барахтайся как хочешь. Но одно дело какая-то third-party хрень, а другое дело runtime языка.
                            • 0
                              Как вы из этого:
                              > Если я делаю reconnect при обрыве сокета, то 99.9% мне всеравно почему это произошло, закрыл ли это соединение внешний сервер, произошел ли таймаут, ОС решила грохнуть и т.п. я просто среагирую на ошибку и сделаю переподключение.

                              сделали:
                              >Почему вы думаете, что у всех такой примитивный use case, когда можно просто грохнуться и ругнуться юзеру в консоль

                              КАК?!?!

                              Если вы пропускаете то что вам в 100 пикселях выше написано, то о чем тут вообще речь может идти?

                              Вы прикапались к документации, я вам 3 раза сказал, что во всех 8!!! случаях кастомная ошибка называется pkg.Error зачем эту информацию дублировать в каждом методе?, в других случаях (741) это стандартная ошибка из пакета errors. И описал 2 (на самом деле 1) пакета, где действительно нужно делать type assertion и смотреть на что-то отличное от err.Error()
                              Даже количественно вы делаете вывод о всей документации, по аргументу относящемуся к 1% всей стандартной библиотеки. Не говоря уже про смысл того что вы «предъявляете».

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

                              > Но это признак плохой и, попросту, отсутствующей документации. Ее просто нет.
                              Ещё раз голословное утверждение

                              > а другое дело runtime языка.
                              это не рантайм

                              А теперь по вашему примеру:
                              > Простой пример, у меня были задачи, когда биндинг происходит на конкретный адаптер.
                              На конкретный адрес наверно все же. Речь про исходящее соединение?
                              > Если этого адаптера нет
                              если логика именно такая, то есть вы таки проверяли существование такого адреса? хм… по тексту ошибки? или все таки перебрали все адреса и проверили есть он там или нет? что-то из этой серии http://play.golang.org/p/BDt3qEQ_2H
                              А если он все же есть, но этот порт занят, так и продолжали долбиться в него?

                              — В общем, вы абсолютно игнорируете то, что вам пишут, приписываете мне слова которых я не говорил, а также путаете рантайм и стандартную библиотеку…
                              • 0
                                Забиндится на конкретный адаптер означает, что если у вас в компе две сетевухи, то вы хотите, чтобы ваше приложение работало через строго определенную сетевуху (которая, в свою очередь, имеет определенный адрес), а не через ту, на какую бог на душу положит.

                                Это может требоваться, например, если система, к которой вы коннектитесь как-то различает клиентов по их remote-адресам, без различия портов (например, предполагается, что коннектится можно только со строго определенного порта). Пример такой Standalone-программы — ForceBindIP.
                                • 0
                                  1) насколько я знаю SO_BINDTODEVICE не поддерживается в windows, а раз вы привели мне пример программы именно под нее, то я не понимаю почему вы говорите забиндится на «определенную сетевуху»
                                  2) bind не подразумевает указания направления, сокет можно привязать и тот который слушает и тот который «подключается»
                                  3) спасибо конечно… но я как бы догадался…

                                  И я напомню речь про Go, его документацию и почему же в методе net.Listen явно не указано что там возвращается не просто error, а net.OpError и в каких реально кейсах эта информация важна.
                                  И то что я говорил:
                                  1) это информация не нужна в 99.9% случаях
                                  2) она не поможет если вы попали в этот 0.1%

                                  вот пример кода http://play.golang.org/p/vf0SO6__1S
                                  e := err.(*net.OpError).Err.(*os.SyscallError).Err.(syscall.Errno)
                                  вот такая конструкция получает первоначальную ошибку (при условии что мы ещё и в типы попали правильно, по хорошему это должно быть лесенка из 3х if), так это выглядит на linux:
                                  99
                                  cannot assign requested address
                                  End

                                  а если не кастить (тоесть вывести текст первой ошибки):
                                  dial tcp 192.168.155.104:6000->192.168.155.104:5000: bind: cannot assign requested address
                                  End

                                  а теперь на windows:
                                  10049
                                  The requested address is not valid in its context.
                                  End

                                  То есть как видим и коды и текст разные.
                                  А найти где же формируется текст ошибки, то для linux это исходники самого go, а для windows это система (через вызов FormatMessageW. хорошо хоть язык всегда английский)…
                                  Так что даже зная все типы вам это не сильно то и помогает…
                                  • 0
                                    3) спасибо конечно… но я как бы догадался…

                                    Просто мне показалось из вашего комментария, что вы это не поняли. Но сейчас перечитал — не все так однозначно :)

                                    То есть как видим и коды и текст разные.

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

                                    2) она не поможет если вы попали в этот 0.1%

                                    В таком случае это косяк библиотеки языка — зачем ему вообще сообщать о каких-то ошибках, если невозможно понять, что произошло? Сразу краши программу и дело с концом — все равно способа понять, что именно пошло не так, не существует, а значит и действий никаких предпринять невозможно, а раз так, то и сообщать о чем бы то ни было нет надобности.
                              • 0
                                прикольно, я видимо понял «я просто среагирую на ошибку» вы буквально что ли это восприняли?
                                То есть я уже 30 лет не сплю ни секунды и слежу за логом ошибок каждой своей программы? и они все с gui и я жму там кнопку переподключиться?
  • +24
    Дизайн ошибок в Go конечно очень странный и таки не выглядит лучше того, что есть у других. Такое ощущение, что сделали лишь бы чтобы было по-другому. Все аргументы, что он чем-то лучше исключений и как-то способствует лучшему коду, не выдерживают критики. Более того, исключения таки, объективно, больше способствуют написанию кода, который обрабатывает ошибки и правильно на них реагирует, и банально более функциональны:
    1. Исключение без try/catch сразу завалит программу, а таки пустой try/catch вокруг каждого куска кода никто не ставит. Ошибки Go можно банально игнорировать и это является поведением по-умолчанию, в отличие от исключений. Самый прикол сразу в hello world — мало кто подозревает по первости, что банальный Println возвращает ошибку. Кто ее обрабатывает? Да никто. Это прям как коды ошибок в C.
    2. Исключения имеют контекст по-определению — стек вызовов. Если go код упадет из-за необработанной ошибки, то где и как искать злосчастное место? Да никак. Panic и recovery не более чем костыль, который выглядит как try/catch, но похоже намерено сделаны уродливыми и неудобными, чтобы их по-меньше использовали.

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

    У меня вот вопрос к опытным гуфам. Допустим, работаю я с пакетом net. Вижу, что метод Listen может мне вернуть ошибку. Естественно возвращают мне интерфейс error, тут все ок. Вопрос, как мне узнать, а какой конкретно тип может иметь эта ошибка? В net пакете пачка этих ошибок разных, но в документации ни слова о том, какие из них может вернуть Listen. Опять же, исключения обычно (а в том же msdn для .net всегда) оговорены конкретно для каждой функции. Как мне вот быть, может я совсем ослеп и где-то таки эта информация есть? Я понимаю, что можно посмотреть исходник, но это, извините, полное извращение. Или же это очередная уникальная особенность экосистемы Go?
    • –20
      Такое ощущение, что сделали лишь бы чтобы было по-другому.

      Вы вправду верите, что Go делали, руководствуясь такими соображениями?

      Все аргументы,… не выдерживают критики.

      Выдерживают.

      Кто ее обрабатывает? Да никто. Это прям как коды ошибок в C.

      Вы сейчас мне рассказываете как *на самом деле* обстоят дела с обработкой ошибок в Go?

      Panic и recovery не более чем костыль

      Опять 25. Panic — именно та ситуация, когда произошла неотвратимая ошибка, и нужно завершить программу и вывалить стек.

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

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

      Я понимаю, что можно посмотреть исходник, но это, извините, полное извращение

      Это в других языках извращение. В Go исходники всегда доступны в godoc и это вопрос миллисекунд. Попробуйте, прежде чем судить.
      Но вообще, хорошую идею подали. Хотя подобная ситуация никогда в практике не была проблемой, но это интересный кейс для статического анализа.
      • +21
        Вы вправду верите, что Go делали, руководствуясь такими соображениями?

        Приходится, потому что объективных причин нет

        Выдерживают.

        Одна из основных идей отказа от исключений в Go в том, что последние подталкивают к коду, который просто игнорирует ошибки. Пустой try/catch и в релиз. Ага, при этом в Go игнорирование ошибки это поведение по-умолчанию. Нет объективных преимуществ у Go ошибок. Напротив, видны объективные недостатки, которые проистекают от того, что Go использует всего лишь немного приукрашенные C ошибки, которые никогда не были удобными и всегда вели к страшному и сложному коду, который и можно видеть в статье. Это кстати одна из причин, почему goto таки очень нужная вещь именно в C коде и ему подобных. Иначе получается страх и ужас как в статье с тучей err != 0 по всему коду.

        Опять 25. Panic — именно та ситуация, когда произошла неотвратимая ошибка, и нужно завершить программу и вывалить стек.

        Да что вы говорите. Вы блоги то читаете? Panic рекомендуется использовать в тех же библиотеках, есть даже конкретный паттерн, который приводится в пример. И самый явный пример это парсерсы, которые активно пользуются этими уродливыми исключениями. Что не советуют — это вываливать panic наружу, а выдавать error.

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

        После «вставка в середину массива уролива, чтобы показать сложность операции» у меня нет причин думать иначе. Это похоже философия языка, делать уродливым и сложным то, что делать не рекомендуется. И защитники Go это активно проповедуют, судя по всему. Таких маленьких деталей в языке достаточно, которые намекают — так не делай. Тот же рандомизированный select — явно намекают, что не надо тебе надеяться, чтобы кто-то будет первый. Ты лучше архитектуру поменяй, чтобы этого не требовалось. Ну или делай вложенные select, что, о боже как неожиданно, уродливо.

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

        Я пробовал, не надо за меня додумывать. Я пробовал, я писал и я плевался. Для чего нужная документация? Как ни странно, чтобы описать, что делает функция, что она принимает, что она возвращает в случае успеха, что она возвращает в случае ошибки. Так делают ВСЕ. Что делает официальная документация Go? Она просит лезть в исходник. Это извращение и никак иначе. И то, что люди привыкают лезть ради таких вещей в исходник, делает только хуже. Можно похоже уже не писать документацию — лезь в исходник, че, нафиг тебе еще объяснять поведение функции.
        • –24
          Вы вправду верите, что Go делали, руководствуясь такими соображениями?

          Приходится, потому что объективных причин нет

          Ок, верьте во что хочется. Это не уровень технических дискуссий.

          Ага, при этом в Go игнорирование ошибки это поведение по-умолчанию

          Это не так. Неясно, зачем вы пытаетесь насадить своё мнение мне.

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

          У panic есть несколько use-кейсов, я вам говорю об одном, вы перепрыгиваете на другой.

          Нет объективных преимуществ у Go ошибок.

          [mirror mode on]
          Есть объективные преимущества у Go ошибок. :)
          [mirror mode off]

          Тот же рандомизированный select — явно намекают, что не надо тебе надеяться, чтобы кто-то будет первый.

          Что? Вам уже и select не угодил? :) Боюсь представить, что вы там за «архитектуры» понапридумывали и вините язык.

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

          Кажется я начинаю понимать… )

          Это извращение и никак иначе.

          Ну раз никак иначе, то тут я вам не помогу.
          • +20
            Действительно не уровень технических дискуссий. Ваши ответы почему-то не содержат внятных объяснений и чётких обоснований почему в данном конкретном случае лучше вот так, а не эдак.
            • –1
              Я даже специально тег вставил (mirror mode), чтобы показать уровень аргументации автора комментариев.
              • +4
                Так все таки вы ребенок или тот, кто писал прошлую статью? Задача авторов языка и вас, как его самопровозглашенного защитника, показать, по каким причин он в чем-то чем-то лучше. Я прошу — покажите. Что я видел выглядит неубедительно, а для некоторых вещей вообще нет объяснения четкого. Вы в ответ что-то про веру, про попугаев и т.д. Может вы уже перестанете дурака валять или просто закончим разговор, раз вам нечего ответить? Если вы верите, что у ошибок Go есть объективные преимущества, но не можете их назвать, то давайте сразу закончим, ибо это не техническая беседа. Живите сами со своими бесами в голове.
                • –9
                  Так все таки вы ребенок
                  Задача авторов языка и вас, как его самопровозглашенного защитника, показать

                  Creker, не знаю, с чего вы решили, что я вам что-то лично должен доказывать и показывать, но уверяю, что это заблуждение. Ни авторы языка, ни я лично ничего вам не должны. Я с вами и с lair тут общаюсь исключительно ради количества комментариев под статьей, ни на что большее такой уровень комментариев, увы, не годится — неужели было непонятно )

                  Эта статья в хабе Го, она для программистов. Если вы интересуетесь Go и что-то не поняли — задайте вопрос, а не пишите, что это «херота» и «полное извращение и никак иначе».
                  • +7
                    Я задал уже кучу вопрос, но них все ответы в стиле «так надо, ваши проблемы». Я спросил про документацию ошибок, мне ответили «так надо, смотрите код». Я спросил про преимущества и причины дизайна ошибок, мне ответили «так надо, вы ничего не понимаете». Сколько еще мне надо наводящих вопросов задать, чтобы добиться конкретного ответа?

                    Ни авторы языка

                    Авторы языка именно должны, если они поставили задачу популяризировать свое детище. Они это делают, они показывают, рассказывают, почему зачем и как. И конкретно дизайн ошибок меня ничем не убедил, что он чем-то лучше. Я это конкретно написал, я написал конкретно почему. Что вы мне ответили? «Выдерживают». Вам нечего ответить или вы хотите просто оставить плохое мнение о себе, о вашей статьей и языке? Я уже писал и еще раз напишу — это просто слепая вера. По крайней мере все ваши комментарии написано в таком ключе.

                    не пишите, что это «херота» и «полное извращение и никак иначе».

                    Если вас задевает мое мнение о языке, то это ваши проблемы. В простонародье это называется «фанбой», «батхерт» и прочие замечательные термины, которые отлично показывают, когда человек не может абстрагироваться от объекта обсуждения, что принимает все на свой счет. Меня крайне удивляют некоторые решения и выражаю это в культурной, но яркой форме такими словами как «херота» и «извращение», что бы кратко и емко показать степено моего удивления
                    • +3
                      > Я задал уже кучу вопрос, но них все ответы в стиле «так надо, ваши проблемы».
                      Вы задали кучу вопрос в стиле «почему в Go не так как я привык делать». «Почему документация написана не так как я привык» и т.п.
                      Параллельно с этим вы ещё на людей кидаетесь, пытаясь их оскорблять
                      • +4
                        Так а почему бы не задать вопрос, почему все пишут подробную документацию, а авторы Go решили отделаться только назначением функции?
                        Почему бы не задать вопрос, почему был выбран конкретный дизайн ошибок, если он объективно проигрывает тем же исключениями? Почему при этом так легко проигнорировать ошибку?
                        Это какие-то запретные вопросы или все таки все в этом мире познается в сравнении? Когда для чего-то нет объективных причин, то встает резонный вопрос, а зачем тогда это сделано было? Вполне справедлив ответ — так захотелось. Рандомизированный select, с ответа Пайка, именно в ключе «так захотелось» сделан. Go похоже этим и живет, но меня пытаются убедить, что за всем скрывается глубокий смысл, который никто называть не хочет.

                        Параллельно с этим вы ещё на людей кидаетесь, пытаясь их оскорблять

                        Когда человек валяет дурака и ведет себя как малое дитя, особенно в технических обсуждениях (а его попытки пошутить точно так же тянут на попытку оскробления), я поступаю по-разному. Иногда просто игнорю человека, раз его не вразумить. Иногда подыгрываю как сейчас и намерено провоцирую в ответ, чтобы получить забавную реакцию, что сейчас успешно получается делать. Разговор давно ушел из разумного русла, человек с самого начала не хочет отвечать на конкретные вопросы, а лишь отнекивается и отшучивается. Вот и я решил пошутить.
                        • +5
                          По поводу перечисления типов исключений в Go — в нем нет типов исключений, и по сути не очень то подразумевается различие в типах ошибок. если где-то есть что-то больше чем текст ошибки то это делается типом Error в пакете.
                          в Go комментарии это только текст, видимо не очень круто копипастить туда суда прототип интерфейса ошибок.
                          > Почему бы не задать вопрос, почему был выбран конкретный дизайн ошибок, если он объективно проигрывает тем же исключениями?
                          он не проигрывает, с исключениями проще раскручивать стек, например это не привычно при реализации парсеров, когда есть 100500 методов и по сути на один внешний вызов получается приличная вложенность вызовов внутренних,
                          но в принципе для ошибок это порождает несколько большее кол-во if'ов внутри реализации не более
                          значения-ошибки это быстро, очень, а ещё позволяет всю вашу программу заинлайнить, ну и реально это более явный способ говорить о том что этот метод может возвращать ошибку, чем исключения в большинстве языков
                          > Почему при этом так легко проигнорировать ошибку?
                          Официальный ответ, что выбор все равно должен оставаться за разработчиком. по сути есть только один случай когда можно сказать что это не так (когда вы не используете результат работы функции), но за запись
                          _, _ = fmt.Println(«Hello World»)
                          я думаю авторов бы прокляли куда больше
                          > Рандомизированный select, с ответа Пайка, именно в ключе «так захотелось» сделан
                          эм, а какой он должен быть? то есть у вас 2 процесса условно одновременно пишут в разные каналы, все равно какой-то пишет первее, планировщик переключает на код который ожидает это событие, пока он это делает там уже готово другое событие, но мы уже обрабатываем это или все процессоры были заняты и у нас 2 события ожидают обработки, по сути планировщик просто выберет одно из них.
                          Реальный кейс сможете привести когда это становится проблемой?
                    • –3
                      И конкретно дизайн ошибок меня ничем не убедил, что он чем-то лучше.

                      Почему вас кто-то в чём то должен убеждать? Я вас вообще не знаю, я перевёл статью для тех, кто интересуется языком Go. Если вы настолько привыкли к эксепшенам, что готовы свято отстаивать их, переходить на личности и писать простыни гнева — то мне жаль, я вам тут не помогу. Тем же, кто действительно хочет понять решения Go совет простой — начните писать код, всё станет намного понятней, я лишь могу помочь прояснить некоторые детали.

                      я написал конкретно почему. Что вы мне ответили? «Выдерживают».

                      Вы обманываете себя. Вы написали без доводов «Не выдерживают критики». Я, чтобы подчеркнуть несостоятельность такого аргумента, вам парировал — «Выдерживают».

                      и выражаю это в культурной… форме такими словами как «херота» и «извращение»

                      У нас разные представления о культуре.
                      • +4
                        Почему вас кто-то в чём то должен убеждать?

                        Я вас не просил отвечать мне. Я написал вопрос, дал аргументацию моей позиции. Вы зачем-то ворвались и вбросили «Выдерживают». Хотите помочь — аргументируйте позицию, если уж удосужились ответить.

                        Если вы настолько привыкли к эксепшенам, что готовы свято отстаивать их, переходить на личности и писать простыни гнева

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

                        простыни гнева

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

                        Вы обманываете себя. Вы написали без доводов «Не выдерживают критики». Я, чтобы подчеркнуть несостоятельность такого аргумента, вам парировал — «Выдерживают».

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

                        У нас разные представления о культуре.

                        Мата нет — значит культурная форма. Представления они общие.
                        • –1
                          Мата нет — значит культурная форма.

                          Таки очень разные.
                          Заканчиваю общение с вами, и во всех будущих статьях тоже.
                          • –2
                            Так а зачем было начинать общение, если вы не способны ответить на вопросы без перехода на личности и игры в попугая? Теперь будете хоть знать, что я привык аргументировать свою позицию, что показано в самом первом комментарии этой ветки. Было дано целых два аргумента, которые, впрочем, почему-то проигнорированы.
                      • +4
                        Давайте, напоследок, разрыв шаблонов устрою. Go мне нравится, и я пишу на нем. Именно поэтому я начал комментировать статьи о нем. Невероятно, правда?
                        • –5
                          Вы ещё на нём не пишете, но видно, что смотрите, пописываете что-то, но пока ещё боретесь с языком )
                          Если интересно действительно освоиться в Go, а не навязать свое мнение про «извращения», заходите в наш Слак-чат, там всегда помогут: 4gophers.ru/slack
                          • +3
                            а Вы что на Go уже написали? Ссылки на проекты, github?
                            • +3
                              На Go пишу по работе уже 2.5 года. Несколько open-source проектов есть, конечно же, тоже: github.com/divan
                              • +2
                                вот проекты у вас однозначно лучше, чем статьи, уж простите
                                я бы с удовольствием послушал например статья о том, с какими проблемами/ограничениями в Go вы реально столкнулись и как их решили

                                да и как я вижу для обработки ошибок вы сами используете err:=nil ;)
                                • –4
                                  да и как я вижу для обработки ошибок вы сами используете err:=nil ;)

                                  Кажется, хабрачитатели реально не поняли статьи и нужно объяснение.

                                  Если честно, когда я первый раз читал статью (когда она вышла), мне она показалась странной — рассмотренный пример сам собой напрашивался на сравнение с эксепшенами, и поэтому было похоже на костыль. Но, дав некоторое время уложиться информации, впитать её, до меня дошло, почему Пайк говорит именно этими словами (про значения) и пытается донести гораздо более важную суть. Это очень крутая статья на самом деле. Показательно, что хабр её не осилил.

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

                                  Я Go принимаю, как есть — без придуманных требований к нему. Он и так мне дал возможность быстро и качественно писать софт, за который я на C++ и даже Python даже не взялся бы, создать свой стартап (только благодаря Go стал возможным, серьезно), и в целом, начать снова получать удовольствие от программирования. Если какие-то есть непонятки — они решаются за минуту и больше не повторяются, каких-то реальных вот «проблем» или «ограничений» я не встречаю.
              • +2
                Автор комментария как раз-таки подробно описал свою точку зрения и технические аспекты вместе с таким выводом. Вы же просто выдаете что-то как истину в первой инстанции, а на все запросы объяснить что-то чуть подробнее (люди реально хотят понять, что, да как, да почему) — вы говорите, что они ни хрена не понимают и сами идиоты (так это воспринимается). Отсюда и минусы, никакие истерики, о которых вы говорите ниже, ни при чем, дело даже не в обработке ошибок в Go, и даже не в самом Go.
                • –1
                  Да ладно вам. Вот смотрите, первый комментарий от lair:
                  Какой механизм не дает программисту забыть вызвать проверку на scanner.Err()?

                  При этом, я понимаю, что автору не интересно на самом деле разобраться, почему это так и почему, если ему кажется, что это ужасно, SpaceX использует Go, а Dropbox вообще всю инфраструктуру, которая молотит экзабайты данных, тоже переписал на Go. Я знаю, что этот пользователь делает отсылку к давней статье о том, что явная обработка ошибок стимулирует не забывать про ошибки, и пытается тут придумать «минус» языка. При всём при этом, lair, у которого 1 пост и ~8000 комментариев, на Go не пишет, но ходит во все посты к Go и ведет себя точно таким же образом.

                  Тем не менее, я совершенно спокойно ему отвечаю как есть на его «технический» вопрос:
                  Документация. Это же библиотека. а не конструкция языка.

                  И это действительно так. Речь тут о библиотечной функции, с которой, прежде чем воспользоваться, нужно познакомиться — из документации или какой-нибудь статьи, но, в основном, конечно же, из документации. Это не часть языка, это решение библиотеки. Пока что оставим в стороне тот мощный посыл, который несет статья на самом деле, пытаясь объяснить этот момент.

                  Ну и что мы видим дальше:
                  И что, это считается хорошим дизайном?

                  Опять же, отсылка к предыдущим постам об «обсуждении» дизайна Go.
                  Ну и сами посмотрите на минусы и плюсы.

                  Кто еще там «хочет понять»? Creker ничего не пытается понять — он пытается доказать, что Go «придумали идиоты» «чтобы было не так, как у других» и это «извращение» и «херота» (цитирую дословно). Это вы называете «люди реально хотят понять»?

                  Вобщем, мне кажется, вы несколько идеализируете Go-троллей. Я с огромнейшим удовольствием помогаю, всем кому могу, но отличать «хочу понять» и «хочу насадить своё мнение» тоже умею. Хотя да, безусловно, ввязываться в перепалки не стоит, тут уж делаю выводы.
    • –1
      в Rust тоже нет исключений кстати, так что не надо говорить, что тут просто решили выделится.
      > Если go код упадет из-за необработанной ошибки, то где и как искать злосчастное место? Да никак
      у Go очень подробные трейсы, если у вас получилось что не обработали ошибку сильно не в том месте где упало, смотрите на это как на стимул обрабатывать ошибки.
      Подход с ошибками можно описать просто — я вижу метод с ошибкой и сразу пишу, что мне делать, что там за ошибка мне в принципе не очень то и важно.
      > Я понимаю, что можно посмотреть исходник, но это, извините, полное извращени
      99% ошибок в go это errors.New(«some text»)
      > Вопрос, как мне узнать, а какой конкретно тип может иметь эта ошибка?
      какой use case, что именно вы смотрите кроме текстовки и как на это реагируете?
      • +3
        у Go очень подробные трейсы, если у вас получилось что не обработали ошибку сильно не в том месте где упало, смотрите на это как на стимул обрабатывать ошибки.

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

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

        В этом вся и проблема ошибок Go для меня. Он подталкивает писать код, который ничего не делает с ошибками. Пишем на best case scenario, а если ошибка, то всем nil вернуть и фиг с ними.

        99% ошибок в go это errors.New(«some text»)

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

        какой use case, что именно вы смотрите кроме текстовки и как на это реагируете?

        Вам придумать use case, когда от ошибки зависит дальнейшее поведение программы? Вы никогда не писали демонов, которые должны сами восстанавливать соединение, повторять то, что завершилось с ошибкой? Куча use case, когда некому и нечему читать сообщения, нужно реагировать и восстанавливаться. И это зависит от того, какая конкретно ошибка вернулась.
        • +2
          > А вот исключение покажет мне конкретное место, кто его выкинул. Как-то нагляднее и полезнее, чем копаться и искать, что же завалило мой Go код
          Идея в том что никто не читает доки и очень дорого выявлять места где вы не так обработали исключение через продакшен для сервисов с большим кол-вом запросов.

          > Они сделаны для того, чтобы кто-то смотрел на тип и реагировал соответственно.
          на что угодно но не на тип, либо сравнение с переменной вроде io.EOF, либо каст к интерфейсу вроде net.Error.

          > Вам придумать use case
          придумывать не надо, давайте на чем-то из жизни всё таки разбираться

          > И это зависит от того, какая конкретно ошибка вернулась.
          да? то есть вы будете перечислять все ошибки почему не получилось забиндить порт? а если вдруг за будите какую?
          • +4
            на что угодно но не на тип, либо сравнение с переменной вроде io.EOF, либо каст к интерфейсу вроде net.Error.

            Скажите это официальным примерам со switch по типам ошибок. Это стандартный паттерн обработки ошибок в Go, если ошибка имеет конкретный тип, а не задана как переменная пакета.

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

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

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

            Собственно, непонятно, о чем вы спорите, если сами создатели языка приводят примеры обработки ошибок switch по типу, switch по значению, но категорически нерекомендуют что-то там парсить в строке ошибок. Наверное они подразумевают, что не все ошибки одинаково обрабатываются. Почему же прим этом такая документация плохая мне не понятно.
            • –2
              > Почему же прим этом такая документация плохая мне не понятно.
              Ещё раз, давайте примеры? Ну и пулриквесты…

              > Если вам в жизни никогда не приходилось по-разному реагировать на ошибки из библиотек, то чего я могу поделать
              Ну давайте вы для начала за себя будет говорить, ок?
              Дело в необходимости, так вот в 99.9% случаев вам это не нужно. А когда нужно уж можно и напрячся и ctrl+click сделать и посмотреть в исходник (благо все в исходниках это тоже часть идеологии) какой там тип… и понять что тип вам не поможет.

              > Но категорически нерекомендуют что-то там парсить в строке ошибок
              естественно, т.к. это чревато для всех api которые для общения с системными вызовами (сокеты, файлы, сигналы и т.п.), т.к. там будут разные сообщения в зависимости от ОС (Go ещё хвалят за легкость написания кроссплаформенного кода) и даже локали
              > Наверное они подразумевают, что не все ошибки одинаково обрабатываются
              Ну может и подразумевают, только из 9 кастомизированных типов ошибок (все кстати называются .Error) на все стандартные пакеты (25мбайт кода)
              только 2 подразумевают, вариативность обработки это net.Error (который интерфейс) и go/types.Error (которая структура), при чем та которая в net ещё и спорно, в остальных случаях они просто добавляют доп. контекст, вроде позиции при парсинге.
              При этом errors.New используется 741 раз, причем в 81 случае это создание единой переменной для всей программы и в подавляющем большинстве случаев это приватные переменные, то есть это сделано не для обработки ошибки, а для экономии ресурсов
              А, что подразумевает обработку делается публичной переменной.
              • +1
                Понятно. Очередной ответ «так надо, вы ничего не понимаете». Для Go уже становится привычным.
                • +3
                  Я вам во всех комментариях ответил по существу вопросов, вы продолжаете в стиле «в Go плохая документация». Это становится привычным. Аргументация?! Не не слышал
      • +1
        например, обломался connect(). если это timeout — попробовать другой сервер сразу, если network unreachable — увеличивающийся таймаут между попытками.
        • 0
          http://habrahabr.ru/post/269909/#comment_8640805
          >И я напомню речь про Go, его документацию и почему же в методе net.Listen явно не указано что там возвращается не просто error, а net.OpError и в каких реально кейсах эта информация важна.

          Так вот по вашему комментарию. Для того чтобы проверить таймаут будет так (err у нас уже есть):
          if err := err.(net.Error); err.Timeout() {
          // это таймаут
          }
          то есть мы использовали ошибку из пакета, а в ней есть метод проверки а таймаут ли это, а вот network unreachable — так:
          if strings.Contains(err.Error(), «network is unreachable») {
          // это network is unreachable… на linux
          }

          да можно придумать логику любой сложности кто спорит то, но вот нужна ли она на самом деле?
          во первых connect() это следствие, чтобы делать именно переподключение, обломаться сначала должно чтение или запись.
          > если это timeout — попробовать другой сервер сразу
          если это первый connect(), а что мешает сделать это сразу? timeout может быть по 100500 причинам на любом из узлов между вами и хостом назначения. если вы на удаленном сервере ничего не хранили, что нет на других и можете так легко делать переподлючения то не проще ли сразу пробовать другой сервер? если данные хранили, то не слишком ли после первого же таймаута их похерить и пробовать другой сервер?
          > если network unreachable — увеличивающийся таймаут между попытками.
          эта ошибка по сути означает что сеть куда вы идете не достижима в текущем состоянии интерфейсов, то есть грубо говоря уборщица вытащила кабель, какой смысл увеличивать таймаут?
          • +2
            Вот уже началось — надо знать, что это net.OpError, потому что явно err.Timeout() не определён во всех ошибках. Или во всех-всех?

            А за strings.Contains для фиксированных ошибок надо бить по наглой рыжей морде.
            • –3
              > Вот уже началось — надо знать, что это net.OpError, потому что явно err.Timeout() не определён во всех ошибках. Или во всех-всех?
              Не надо, в моем коде его нет, во всех пакетах стандартной библиотеки где генерируется свой тип ошибок (во всех 8), это [имя пакета].Error
              > А за strings.Contains для фиксированных ошибок надо бить по наглой рыжей морде.
              за тугодумство и over engineering я бы может тоже бил по… лу, но это не относится к теме
        • +1
          если скажите что пример с «network unreachable» был про Android — ок (надо бы глянуть конечно что там вернется на всякий), но типизация опять же не оч. помогла.
          ну и там вроде есть встроенные способы проверять а подлючены мы во внешний мир или нет
  • +5
    Недавно мы просканировали все open-source проекты, которые мы только смогли найти и увидели, что этот сниппет появляется лишь раз на страницу или две, гораздо реже, чем многие могли бы подумать.
    Видимо многие игнорируют ошибки и пишут «не безопасный» код.
    В январской статье уже был спор об этом, где привели исходник на Го, в котором «мало» проверок — а точнее более 150 строк на 1200 строк исходника, что-бы только проверить и передать ошибку, кстати «ваш» подход там вроде тоже используется.

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

    Я использую Го как вспомогательный язык, что-бы ускорить некоторые кейсы, т.е. нужна бо'льшая скорость, и как раз, проверка ошибок работает быстрее чем исключения — и это для меня как одна из «отговорок» почему проверки в Го оправданы.

    Так же можно попробовать сделать умную генерацию кода, которая будет трансформировать throw/try/catch в if/return.
  • –2
    Судя по статье основная проблема Go в том, что его сравнивают с современными высокоуровневыми языками вроде C++, C#, Java. Корректнее было бы сравнивать Go с C. Если бы мне пришлось писать что-то, что обычно пишется на C (например утилиту под *nix или какой-нить embed), то я определенно взял бы Go и это дало бы большой выигрыш в продуктивности.
    • +8
      embed с garbage collector? удачи
      • –2
        Нормально работает) в embox есть урезанная jvm — используется и работает) Просто и embeded сейчас уже не тот, что раньше — он «подрос» и по вычислительным мощностям и по потребностям.
        • 0
          ну я к тому, что человек может не знал что в go есть gc, и не совсем подходит язык для embedded
          • –3
            Ну сейчас и термин embedded достаточно размытым стал.
            К примеру для многих любительских платформ (вроде Raspberry) Go достаточно активно пользуют, для дронов есть прошивки на Go и тд.
            gobot.io
            embd.kidoman.io
      • 0
        А в чем проблема?
      • 0
        embed сейчас даже на JS пишут.
    • +5
      Поддерживаю Вас всеми руками. А то надоел уже этот спор вокруг GO и «остального мира ЯП». Нет идеального языка программирования — у каждого своя ниша и свои преимущества/недостатки. Я не являюсь «профессионалом» в GO, но это не помешало мне за 15 минут написать простенькие тесты для отладки «железяки». По моим прикидкам написать подобное на С (который является моим основным рабочим инструментом) за то же время не получилось бы… И еще один довод в пользу GO: я долго мучился в процессе обучения программированию одной моей сотрудницы, ну не получалось у нее перекладывать алгоритмы в код программ на С. Я предложил ей реализовать свое задание на GO и, о чудо, у нее начало получаться. Из этого можно сделать некоторые выводы…
      Я не считаю GO идеальным языком программирования, но он занял свое место среди моего «ящика с инструментами».
      • 0
        вот кстати из статьи в статью упоминается про нишу Go — так а какая она?
        На Go я писал только демоны-воркеры для RabbitMQ да и всякие тесты. Причем с таким же успехом приходится писать все это и на Java и честно говоря, Java ближе
        • +1
          Посмотрите вот эту статью: dave.cheney.net/2015/10/30/programming-language-markets
          • 0
            ну в принципе мнение автора статьи с моим совпадает, что Go, больше для инфраструктурных и серверных задач. Было бы конечно круто, чтобы и в вебе он развивался, а то пока для веб-разработки вспоминает только Revel или собирать самому из библиотек
    • +1
      Но ведь go гораздо более высокоуровневый чем C++…
      Если уж есть место куда можно вместо C вставить Go, то и C++ подойдет с тем же успехом по-моему.
      • +2
        Я не считаю Go высокоуровневым языком. Из высокоуровневого в Go только горутины и channels, в остальном язык очень низкоуровневый. И далеко не любой код на C++ может быть переписан на Go.

        При этом Go — очень практичный язык (даже не столько язык, сколько экосистема в целом), не javascript конечно, но близок к нему. Он идеально подходит как замена C. C++ тоже подходит как замена C, но C++ в разы менее практичен.
        • +3
          Извините, а не могли бы вы назвать признаки языков низкого уровня? А то мы о разных вещах говорим по-моему.
          Плохо себе представляю ядро ОС или драйвера на go
          • 0
            Вот, возможно будет интересно видео на тему:
          • 0
            Высокоуровневый язык скрывает сложность от программиста и имеет средства, которые позволяют строить модули, скрывающие эту сложность. Низкоуровневые языки наоборот — выпячивают детали реализации в погоне за эффективностью кода и скоростью компиляции.

            В Go есть высокоуровневые фичи — Gc, каналы и select, горутины, но почему-то отсутствует обобщённое программирование, монады, полноценное ООП. Зато куча низкоуровневых — массивы и слайсы, обработка ошибок, пустые интерфейсы (привет void *).
            • –1
              Массивы — низкоуровневая фича? А в каком языке из перечисленных (С++, C#, Java) их нет? Слайсы — опять-таки что в них низкоуровневого?
              Получаем язык высокого уровня с обработкой ошибок (и пустыми интерфейсами). И при том медленнее как С, так и С++.
        • +1
          > Он идеально подходит как замена C
          Говоря о «замене С» подразумевается производительность. Можете привести какие-то бенчмарки? Иначе, на чем строиться ваше предположение? Из собственных наблюдений — Go в разы быстрее Python-а и т.п., но до производительности С ему очень далеко. Особенно когда в коде появляются го-рутины и каналы (а без них, на мой взгляд, использовать Go вообще нецелесообразно) — производительность сразу проседает в разы.
        • +1
          offtop:
          JS — практичный?.. WAT.
  • +25
    Дорогой автор, мне кажется своими статьями и комментариями Вы сделали больше для депопуляризации Go больше, чем любая критическая статья, которую видел до этого
    • +5
      +1
    • +6
      мне кажется плох тот инженер, который составил свое мнение на основе каких-то статей или (внезапно) комментариев, а не потому, что честно взял и сам попробовал.
      • +7
        ну тут спору нет, проблема в том, что само желание пробовать может пропасть
        • 0
          Вам для мотивации нужно побольше комментов да лайков? Ну-ну =)
      • –1
        А речь о молотке, которым лично инженер забивает гвоздь? Об экспериментальной реализации какой-нибудь новинки computer science? Или всё-таки о языке, на котором люди пишут? Причём пока тот язык незнакомый, легче оценить то, что те же люди пишут на других языках (в данном случае, на русском).
      • –2
        инженер — это не полугодовалый ребёнок, что бы всё подряд пробовать
    • –2
      Я вами не согласен. То, что у небольшой группы хабровчан начинается истерика, не отменяет факта пользы переводов материалов по Go — всё таки это остается в поиске, это гуглится и так далее.
      • +1
        Значит материал очень неоднозначный, переводили бы Effective Go тогда, пользы было бы больше
        • –5
          Материал этот полезный, и ценный. Уж фильтровать статьи для перевода по признаку «как на них отреагируют Go-хейтеры» я точно не буду.

          Effective Go не тот формат для Хабра.
          • +4
            Я бы с удовольствием прочитал. Хоть и сам могу понять на оригинале, но интересует больше видение и вставки переводчика. Да и коментарии… Зачастую от них даже пользы больше)
      • +10
        Меня удивляет, что конструктивную, в общем-то, критику вы выставляете как неадекватные выкрики Go-хейтеров. И тонкие намеки в виде десятка минусов, вы, видимо, воспринимаете как гонения теми самыми хейтерами.
        Попробуйте честно и технически конструктивно отвечать на вопросы, даже если они выставляют Go не в лучшем свете, что в первом комментарии за вас более-менее сделал neolink
        Аргументы ad hominem тоже чести вам не делают.
        При этом перевод-то неплохой и исходная статья хоть и спорная, но интересная.
        ИМХО.
        • –5
          Ну нет, когда люди, незнакомые с языком, пишут под каждым постом десятки комментариев, пытаясь доказать, что Go неудобен и его придумали извращенцы — это ниразу не «конструктивная критика», уж простите.

          Но то, что мне не стоит влазить с ними в перепалки — соглашусь, конечно.
          • +8
            Если тема достаточно подробно рассмотрена в статье, то необязательно знать весь язык, чтобы ее обсудить.
            Если конкретно описанный в статье принцип выглядит неудобным и нелогичным и это обоснованно аргументами — это конструктивная критика.
  • +1
    А знает кто-то библиотеку для работы с ошибками — чтобы при создании ошибки у неё внутри сохранялся бы стектрейс и вложенная ошибка — чтобы потом можно было легко понять где она возникла даже если ошибка уже на несколько уровней вверх прокинута?
  • +1
    Я тут недавно задавал вопрос на stackoverflow — по поводу того, почему компилятор Go не заставляет нас проверять ошибки (или хотя бы эксплицитно отказываться от их проверки).

    В комментариях сказали, что этого много кто хочет (есть куча тредов в мейлинг-листе Go), но из-за невозможности что-либо проверять в defer-конструкциях это вряд ли будет сделано.

    Вообще, конечно, я не уверен в том, что ошибки в Go — это удобно. Но пока живем, вроде.
    • 0
      Есть ситуации, когда вам действительно не нужно проверять ошибки. Например, у вас простенькая программка, читающая ввод пользователя из stdin. Вероятностью того, что произойдет какой-то I/O error, который нужно как-то специально обработать, в данном случае можно принебречь. Ровно как и с выводом в stdout. Язык должен стимулировать обрабатывать ошибки, но не запрещать. Кажется, статью очень многие вообще не поняли. На medium-е написал попытку объяснения, переведу на днях.

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