(Перевод статьи из официального блога 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_, включая небольшое записанное им видео, посмотрите его блог.

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


  1. lair
    01.11.2015 03:29
    +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, ее все-таки решили проигнорировать.


    1. divan0
      01.11.2015 03:41
      -19

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

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


      1. lair
        01.11.2015 03:44
        +17

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

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


        1. divan0
          01.11.2015 03:57
          -12

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

          Считается ли документация хорошим дизайном библиотеки?
          Да, рекомендую попробовать писать документацию.
          В Go, кстати, для этого достаточно просто написать комментарий над функцией, начинающийся с имени функции — и готовая удобная HTML-страничка готова или доступна через godoc.org.


          1. lair
            01.11.2015 04:00
            +16

            Нет, считается ли хорошим дизайном вынос информации об ошибке в отдельный метод (в данном случае — Err), вместо ее возврата сразу в методе, где она возникла?


            1. divan0
              01.11.2015 04:02
              -8

              Статья именно об этом. Вы её не прочли или не поняли?


              1. lair
                01.11.2015 04:04
                +9

                У меня из статьи создалось ощущение, что да, это считается хорошим дизайном. Я все правильно понял?


                1. divan0
                  01.11.2015 04:17
                  -7

                  Нет, вы совсем неправильно поняли.


                  1. creker
                    01.11.2015 04:45
                    +16

                    Эм, а как тогда понимать код в статье? Мне говорят, что надо не сразу получать ошибку и ее обрабатывать, а пробежаться все сотни и миллионы итераций с no-op, чтобы потом в конце узнать, что оказывается на первой итерации была ошибка и мой цикл в пустую делал работу. Это херота какая-то и ужасный просто совет. Я удивлен, что такое советуют в официальном блоге. Ошибка должны быть обработана в том месте, где она произошла. Если код из-за этого становится страшным, то, ну не знаю. Вроде бы давно ясно, что обработка ошибок многословна. Либо надо таки как-то рефакторить код. Но не делать костыль как в статье.

                    Каким образом совет в статье связан с «программирование с использованием значений ошибок» решительно не ясно.


                    1. divan0
                      01.11.2015 05:03
                      -14

                      Ну вы хоть прочитали, и не поняли, это простительно.

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

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

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

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


                      1. creker
                        01.11.2015 05:07
                        +7

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

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

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


                        1. divan0
                          01.11.2015 05:13
                          -11

                          а пробежаться все сотни и миллионы итераций с no-op

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

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

                          Покажите «прямой текст» где вам говорят «один вариант плохой, другой хороший».


                          1. creker
                            01.11.2015 05:25
                            +9

                            Так кто же не читал собственную статью

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

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

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

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


                            1. VolCh
                              01.11.2015 07:50

                              1. «Выглядит более естественным» != «лучше»

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


                              1. qw1
                                01.11.2015 11:02
                                +6

                                он широко используется и в других языках, в частности в С

                                Чтобы было по-сишному, ф-ция Scan должна возвращать не boolean, а одно из {DATA_FETCHED, EOF, ERROR}.
                                В этом случае минимальный цикл оставался бы таким же простым (while Scanner.Scan()==DATA_FETCHED), но из интерфейса было бы кристально понятно, что может быть как ERROR, так и EOF, не надо смешивать эти ситуации


                                1. neolink
                                  01.11.2015 11:58

                                  Scanner скрывает EOF, поэтому ваш интерфейс скукоживается до {DATA_FETCHED, ERROR}, что суть есть bool.
                                  оригинальная претензия все равно про то что проверка будет после цикла, возвращать bool быстрее чем interface{} видимо дело в этом


                                  1. qw1
                                    01.11.2015 12:52

                                    У Scanner плохо то, что даже если я не хочу разбираться c ошибкой и мне достаточно факта её наличия, в любом случае нужен лишний в данном сценарии вызов Err().


                              1. Mingun
                                01.11.2015 11:51
                                +4

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

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

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


                                1. qw1
                                  01.11.2015 13:13
                                  -1

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

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

                                  Всё сразу ясно, но используется это только программистом для диагностики, а когда всё хорошо работает, это лишний код. Возможно, для всяких high load и big data маятник качнулся в обратную сторону — минимум оверхеда на диагностику, минимум информации об ошибках, максимум скорости. Возможно, Go был написан с этими соображениями.


                    1. nwalker
                      02.11.2015 17:20
                      +3

                      Для объективности — миллионов итераций с no-op не будет, Scanner.Scan «returns false when the scan stops, either by reaching the end of the input or an error.» и ошибка будет обработана, если код написан в расчете именно на эту логику — а так как нам нужно ее обработать, мы этот код напишем.


                  1. lair
                    01.11.2015 04:53
                    +3

                    То есть, это не считается хорошим дизайном, не надо так делать?


                    1. divan0
                      01.11.2015 05:00
                      -8

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


                      1. Randl
                        01.11.2015 10:29
                        +9

                        Так просто ответьте на вопрос — пример в статье это хороший дизайн или плохой?


                      1. poxu
                        01.11.2015 10:36
                        +8

                        Ну так напишите какая у статьи тема, чём посыл и какой сделан вывод. Ибо, судя по вашим словам, люди в основном с пониманием этих вещей ошибаются.


                        1. divan0
                          01.11.2015 18:11
                          -6

                          Ну так напишите какая у статьи тема, чём посыл и какой сделан вывод.

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


                          1. poxu
                            01.11.2015 21:43
                            +9

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


                            1. divan0
                              01.11.2015 23:44
                              -10

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


                              1. dordzhiev
                                03.11.2015 20:56
                                +3

                                Ответьте мне тогда, я не lair если что. Очень хочу понять как надо делать правильно, но ваш комментарий поставил меня в тупик.


                                1. divan0
                                  03.11.2015 21:06
                                  -3

                                  Вот целый пост написал с объяснением: habrahabr.ru/post/270027


                      1. lair
                        01.11.2015 12:18
                        +5

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

                        Я, вроде, задал простой вопрос к одному из примеров в статье.


                        1. neolink
                          01.11.2015 18:28
                          +2

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


                          1. lair
                            01.11.2015 19:17
                            +2

                            Ну то есть это все-таки рекомендуемый дизайн?


                            1. neolink
                              01.11.2015 22:00
                              -1

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


                              1. lair
                                01.11.2015 22:03
                                +2

                                divan0 при этом написал наоборот. Обидно.

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

                                (ну и униформность нарушается тоже, но это уже второго порядка)


                                1. neolink
                                  01.11.2015 22:24

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

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


                                  1. lair
                                    01.11.2015 22:29
                                    +5

                                    как обычно есть одно «но», когда вы проигнорировали вообще весь результат функции

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

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


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

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

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

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

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


                                    1. neolink
                                      01.11.2015 22:48
                                      -2

                                      > Да в том-то и дело, что нет.
                                      да я про сам принцип писал, что он не 100% верен.

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


                                      1. lair
                                        01.11.2015 22:59
                                        +6

                                        То есть, нашли пакет, конструктор, у типа нашли метод, нужный, поняли что его надо в условие цикла поставить, и вызывать Token у сканера, не прочитав ни доки, ни описания к методам ни их кода. И с такой проницательностью не поняли что там есть Err?!

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

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

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

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


                                        1. neolink
                                          01.11.2015 23:50
                                          +1

                                          > Ну да, типичное программирование по подсказкам IDE.
                                          Ну ок, только: https://hsto.org/files/b95/67e/a22/b9567ea224c64ab7bed46d4d67cb1ffd.png

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


                                          1. lair
                                            01.11.2015 23:52
                                            +1

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

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


                                            1. neolink
                                              02.11.2015 02:01
                                              +1

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


                                              1. lair
                                                02.11.2015 02:12
                                                +4

                                                Это не 4 типа сообщать об ошибках, т.к. вы Err() посчитали дважды (или для вас bool в Scan и TokenType в Next() это 2 разных способа сообщать об ошибках?!)

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

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

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


                                    1. ramzes_yan
                                      02.11.2015 16:50

                                      прошу прощения что вмешиваюсь в Вашу беседу
                                      у меня лично к Вам lair есть вопрос

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

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


                                      не троллинга ради мне реально интересно


                                      1. lair
                                        02.11.2015 16:56

                                        Я такого не знаю.


            1. VolCh
              01.11.2015 07:57

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


              1. lair
                01.11.2015 12:23
                +7

                Неа. В описанном примере Scan может вернуть false (т.е., по вашему мнению, индикатор ошибки) даже в том случае, когда Err вернет nil (т.е., ошибки не было) — это случится в конце потока. Поэтому индикатором именно наличия ошибки служит сравнение Err с nil

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

                f, err := os.Open("filename.ext")
                if err != nil {
                    log.Fatal(err)
                }
                


                (… точнее, это я думал, что он стандартный. Теперь уже и не знаю, что думать.)


      1. lair
        01.11.2015 12:38
        +10

        Я просто процитирую вашу же статью: «И эта простота и явная невозможность проигнорировать ошибку создает стимул.»

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


        1. divan0
          01.11.2015 19:32
          -4

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


          1. lair
            01.11.2015 19:33
            +5

            Не открыть документацию/не прочитать документацию/неправильно понять документацию — легко. Для того, чтобы проигнорировать ошибку, не надо ничего делать специально — достаточно не сделать обращения к методу.


            1. divan0
              01.11.2015 19:42
              -11

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


              1. lair
                01.11.2015 19:43
                +9

                А что, люди, пишущие на Go, читают документацию как-то иначе, нежели это делают все остальные люди?


                1. Xlab
                  02.11.2015 12:59
                  +3

                  Они как минимум обращают внимание на обработку ошибок от этой самой либы.
                  Мне приходилось писать на c#, java, c++, python и ruby и только перейдя на Go я заметил, что здесь люди готовы обрабатывать ошибки, а не просто игнорить до того момента пока оно само не грохнется.


                  1. creker
                    02.11.2015 13:58

                    Это какое-то универсальное правило или я такой уникальный, что в шарпах ошибки и исключения ловлю? Смотрю в документации, как функция может завалиться.
                    Зато я вот заметил другое — в Go людям плевать на ошибки. И мне здесь уже советовали плевать на ошибки. Все равно их все не обработать.


                    1. Xlab
                      02.11.2015 14:09
                      +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. neolink
                      02.11.2015 14:32
                      +1

                      не надо врать, речь была о типизации ошибок, а не о факте их возникновения


                      1. creker
                        02.11.2015 14:33

                        Я прекрасно помню, о чем была речь. Отсутствие проверок на тип произошедшей ошибки = отсутствие обработки ошибок. Выплюнуть nil и сообщение в консоль это игнорирование ошибки больше.

                        Как еще понимать вот это

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


                        1. neolink
                          02.11.2015 14:47

                          Если я делаю reconnect при обрыве сокета, то 99.9% мне всеравно почему это произошло, закрыл ли это соединение внешний сервер, произошел ли таймаут, ОС решила грохнуть и т.п. я просто среагирую на ошибку и сделаю переподключение.
                          Если нужна доп. инфа, что именно произошло, то я вам привел ВСЕ места в стандартной либе где есть кастомный тип ошибки или интерфейс и указал, что это не сильно то и поможет. потому что если вы хотите отличить то, что порт занят другим процессом, от того что у пользователя прав не хватило забиндится на 0.0.0.0 (честно — не знаю зачем), то это только текст ошибки парсить. а так лучше не делать.


                          1. creker
                            03.11.2015 02:09
                            +5

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

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


                            1. neolink
                              03.11.2015 11:08

                              Как вы из этого:
                              > Если я делаю 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
                              А если он все же есть, но этот порт занят, так и продолжали долбиться в него?

                              — В общем, вы абсолютно игнорируете то, что вам пишут, приписываете мне слова которых я не говорил, а также путаете рантайм и стандартную библиотеку…


                              1. Mingun
                                03.11.2015 18:46

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

                                Это может требоваться, например, если система, к которой вы коннектитесь как-то различает клиентов по их remote-адресам, без различия портов (например, предполагается, что коннектится можно только со строго определенного порта). Пример такой Standalone-программы — ForceBindIP.


                                1. neolink
                                  03.11.2015 19:55

                                  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. хорошо хоть язык всегда английский)…
                                  Так что даже зная все типы вам это не сильно то и помогает…


                                  1. Mingun
                                    04.11.2015 12:51

                                    3) спасибо конечно… но я как бы догадался…

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

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

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

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

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


                              1. neolink
                                03.11.2015 20:11

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


  1. creker
    01.11.2015 04:35
    +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?


    1. divan0
      01.11.2015 04:59
      -20

      Такое ощущение, что сделали лишь бы чтобы было по-другому.

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

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

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

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

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

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

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

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

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

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

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


      1. creker
        01.11.2015 05:22
        +21

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

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

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

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

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

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

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

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

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

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


        1. divan0
          01.11.2015 06:09
          -24

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


          1. poxu
            01.11.2015 10:45
            +20

            Действительно не уровень технических дискуссий. Ваши ответы почему-то не содержат внятных объяснений и чётких обоснований почему в данном конкретном случае лучше вот так, а не эдак.


            1. divan0
              01.11.2015 18:19
              -1

              Я даже специально тег вставил (mirror mode), чтобы показать уровень аргументации автора комментариев.


              1. creker
                01.11.2015 18:27
                +4

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


                1. divan0
                  01.11.2015 18:35
                  -9

                  Так все таки вы ребенок
                  Задача авторов языка и вас, как его самопровозглашенного защитника, показать

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

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


                  1. creker
                    01.11.2015 18:52
                    +7

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

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

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

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

                    Если вас задевает мое мнение о языке, то это ваши проблемы. В простонародье это называется «фанбой», «батхерт» и прочие замечательные термины, которые отлично показывают, когда человек не может абстрагироваться от объекта обсуждения, что принимает все на свой счет. Меня крайне удивляют некоторые решения и выражаю это в культурной, но яркой форме такими словами как «херота» и «извращение», что бы кратко и емко показать степено моего удивления


                    1. neolink
                      01.11.2015 19:34
                      +3

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


                      1. creker
                        01.11.2015 19:48
                        +4

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

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

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


                        1. neolink
                          01.11.2015 20:52
                          +5

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


                    1. divan0
                      01.11.2015 19:40
                      -3

                      И конкретно дизайн ошибок меня ничем не убедил, что он чем-то лучше.

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

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

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

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

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


                      1. creker
                        01.11.2015 19:56
                        +4

                        Почему вас кто-то в чём то должен убеждать?

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

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

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

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

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

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

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

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

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


                        1. divan0
                          01.11.2015 19:58
                          -1

                          Мата нет — значит культурная форма.

                          Таки очень разные.
                          Заканчиваю общение с вами, и во всех будущих статьях тоже.


                          1. creker
                            01.11.2015 20:02
                            -2

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


                      1. creker
                        01.11.2015 19:59
                        +4

                        Давайте, напоследок, разрыв шаблонов устрою. Go мне нравится, и я пишу на нем. Именно поэтому я начал комментировать статьи о нем. Невероятно, правда?


                        1. divan0
                          01.11.2015 20:04
                          -5

                          Вы ещё на нём не пишете, но видно, что смотрите, пописываете что-то, но пока ещё боретесь с языком )
                          Если интересно действительно освоиться в Go, а не навязать свое мнение про «извращения», заходите в наш Слак-чат, там всегда помогут: 4gophers.ru/slack


                          1. andrewnester
                            01.11.2015 21:53
                            +3

                            а Вы что на Go уже написали? Ссылки на проекты, github?


                            1. divan0
                              01.11.2015 23:46
                              +3

                              На Go пишу по работе уже 2.5 года. Несколько open-source проектов есть, конечно же, тоже: github.com/divan


                              1. andrewnester
                                02.11.2015 00:53
                                +2

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

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


                                1. divan0
                                  02.11.2015 17:10
                                  -4

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

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

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

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

                                  Я Go принимаю, как есть — без придуманных требований к нему. Он и так мне дал возможность быстро и качественно писать софт, за который я на C++ и даже Python даже не взялся бы, создать свой стартап (только благодаря Go стал возможным, серьезно), и в целом, начать снова получать удовольствие от программирования. Если какие-то есть непонятки — они решаются за минуту и больше не повторяются, каких-то реальных вот «проблем» или «ограничений» я не встречаю.


              1. VasilioRuzanni
                02.11.2015 12:52
                +2

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


                1. divan0
                  02.11.2015 17:23
                  -1

                  Да ладно вам. Вот смотрите, первый комментарий от lair:

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

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

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

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

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

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

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

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


    1. neolink
      01.11.2015 11:50
      -1

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


      1. creker
        01.11.2015 16:17
        +3

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

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

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

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

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

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

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

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


        1. neolink
          01.11.2015 17:57
          +2

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

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

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

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


          1. creker
            01.11.2015 18:42
            +4

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

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

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

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

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

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


            1. neolink
              01.11.2015 19:14
              -2

              > Почему же прим этом такая документация плохая мне не понятно.
              Ещё раз, давайте примеры? Ну и пулриквесты…

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

              > Но категорически нерекомендуют что-то там парсить в строке ошибок
              естественно, т.к. это чревато для всех api которые для общения с системными вызовами (сокеты, файлы, сигналы и т.п.), т.к. там будут разные сообщения в зависимости от ОС (Go ещё хвалят за легкость написания кроссплаформенного кода) и даже локали
              > Наверное они подразумевают, что не все ошибки одинаково обрабатываются
              Ну может и подразумевают, только из 9 кастомизированных типов ошибок (все кстати называются .Error) на все стандартные пакеты (25мбайт кода)
              только 2 подразумевают, вариативность обработки это net.Error (который интерфейс) и go/types.Error (которая структура), при чем та которая в net ещё и спорно, в остальных случаях они просто добавляют доп. контекст, вроде позиции при парсинге.
              При этом errors.New используется 741 раз, причем в 81 случае это создание единой переменной для всей программы и в подавляющем большинстве случаев это приватные переменные, то есть это сделано не для обработки ошибки, а для экономии ресурсов
              А, что подразумевает обработку делается публичной переменной.


              1. creker
                01.11.2015 19:50
                +1

                Понятно. Очередной ответ «так надо, вы ничего не понимаете». Для Go уже становится привычным.


                1. neolink
                  02.11.2015 10:31
                  +3

                  Я вам во всех комментариях ответил по существу вопросов, вы продолжаете в стиле «в Go плохая документация». Это становится привычным. Аргументация?! Не не слышал


      1. datacompboy
        04.11.2015 02:23
        +1

        например, обломался connect(). если это timeout — попробовать другой сервер сразу, если network unreachable — увеличивающийся таймаут между попытками.


        1. neolink
          04.11.2015 10:34

          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 — увеличивающийся таймаут между попытками.
          эта ошибка по сути означает что сеть куда вы идете не достижима в текущем состоянии интерфейсов, то есть грубо говоря уборщица вытащила кабель, какой смысл увеличивать таймаут?


          1. datacompboy
            04.11.2015 11:23
            +2

            Вот уже началось — надо знать, что это net.OpError, потому что явно err.Timeout() не определён во всех ошибках. Или во всех-всех?

            А за strings.Contains для фиксированных ошибок надо бить по наглой рыжей морде.


            1. neolink
              04.11.2015 11:54
              -3

              > Вот уже началось — надо знать, что это net.OpError, потому что явно err.Timeout() не определён во всех ошибках. Или во всех-всех?
              Не надо, в моем коде его нет, во всех пакетах стандартной библиотеки где генерируется свой тип ошибок (во всех 8), это [имя пакета].Error
              > А за strings.Contains для фиксированных ошибок надо бить по наглой рыжей морде.
              за тугодумство и over engineering я бы может тоже бил по… лу, но это не относится к теме


        1. neolink
          04.11.2015 10:42
          +1

          если скажите что пример с «network unreachable» был про Android — ок (надо бы глянуть конечно что там вернется на всякий), но типизация опять же не оч. помогла.
          ну и там вроде есть встроенные способы проверять а подлючены мы во внешний мир или нет


  1. lega
    01.11.2015 11:27
    +5

    Недавно мы просканировали все open-source проекты, которые мы только смогли найти и увидели, что этот сниппет появляется лишь раз на страницу или две, гораздо реже, чем многие могли бы подумать.
    Видимо многие игнорируют ошибки и пишут «не безопасный» код.
    В январской статье уже был спор об этом, где привели исходник на Го, в котором «мало» проверок — а точнее более 150 строк на 1200 строк исходника, что-бы только проверить и передать ошибку, кстати «ваш» подход там вроде тоже используется.

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

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

    Так же можно попробовать сделать умную генерацию кода, которая будет трансформировать throw/try/catch в if/return.


  1. gandjustas
    01.11.2015 14:46
    -2

    Судя по статье основная проблема Go в том, что его сравнивают с современными высокоуровневыми языками вроде C++, C#, Java. Корректнее было бы сравнивать Go с C. Если бы мне пришлось писать что-то, что обычно пишется на C (например утилиту под *nix или какой-нить embed), то я определенно взял бы Go и это дало бы большой выигрыш в продуктивности.


    1. andrewnester
      01.11.2015 17:16
      +8

      embed с garbage collector? удачи


      1. Kolyuchkin
        01.11.2015 17:48
        -2

        Нормально работает) в embox есть урезанная jvm — используется и работает) Просто и embeded сейчас уже не тот, что раньше — он «подрос» и по вычислительным мощностям и по потребностям.


        1. andrewnester
          01.11.2015 17:50

          ну я к тому, что человек может не знал что в go есть gc, и не совсем подходит язык для embedded


          1. divan0
            01.11.2015 18:08
            -3

            Ну сейчас и термин embedded достаточно размытым стал.
            К примеру для многих любительских платформ (вроде Raspberry) Go достаточно активно пользуют, для дронов есть прошивки на Go и тд.
            gobot.io
            embd.kidoman.io


      1. gandjustas
        01.11.2015 19:26

        А в чем проблема?


      1. VolCh
        02.11.2015 09:08

        embed сейчас даже на JS пишут.


    1. Kolyuchkin
      01.11.2015 17:42
      +5

      Поддерживаю Вас всеми руками. А то надоел уже этот спор вокруг GO и «остального мира ЯП». Нет идеального языка программирования — у каждого своя ниша и свои преимущества/недостатки. Я не являюсь «профессионалом» в GO, но это не помешало мне за 15 минут написать простенькие тесты для отладки «железяки». По моим прикидкам написать подобное на С (который является моим основным рабочим инструментом) за то же время не получилось бы… И еще один довод в пользу GO: я долго мучился в процессе обучения программированию одной моей сотрудницы, ну не получалось у нее перекладывать алгоритмы в код программ на С. Я предложил ей реализовать свое задание на GO и, о чудо, у нее начало получаться. Из этого можно сделать некоторые выводы…
      Я не считаю GO идеальным языком программирования, но он занял свое место среди моего «ящика с инструментами».


      1. andrewnester
        01.11.2015 17:54

        вот кстати из статьи в статью упоминается про нишу Go — так а какая она?
        На Go я писал только демоны-воркеры для RabbitMQ да и всякие тесты. Причем с таким же успехом приходится писать все это и на Java и честно говоря, Java ближе


        1. divan0
          01.11.2015 18:05
          +1

          Посмотрите вот эту статью: dave.cheney.net/2015/10/30/programming-language-markets


          1. andrewnester
            02.11.2015 01:01

            ну в принципе мнение автора статьи с моим совпадает, что Go, больше для инфраструктурных и серверных задач. Было бы конечно круто, чтобы и в вебе он развивался, а то пока для веб-разработки вспоминает только Revel или собирать самому из библиотек


    1. Randl
      01.11.2015 17:59
      +1

      Но ведь go гораздо более высокоуровневый чем C++…
      Если уж есть место куда можно вместо C вставить Go, то и C++ подойдет с тем же успехом по-моему.


      1. gandjustas
        01.11.2015 18:28
        +2

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

        При этом Go — очень практичный язык (даже не столько язык, сколько экосистема в целом), не javascript конечно, но близок к нему. Он идеально подходит как замена C. C++ тоже подходит как замена C, но C++ в разы менее практичен.


        1. Randl
          01.11.2015 19:56
          +3

          Извините, а не могли бы вы назвать признаки языков низкого уровня? А то мы о разных вещах говорим по-моему.
          Плохо себе представляю ядро ОС или драйвера на go


          1. divan0
            01.11.2015 20:00

            Вот, возможно будет интересно видео на тему:


          1. gandjustas
            01.11.2015 22:48

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

            В Go есть высокоуровневые фичи — Gc, каналы и select, горутины, но почему-то отсутствует обобщённое программирование, монады, полноценное ООП. Зато куча низкоуровневых — массивы и слайсы, обработка ошибок, пустые интерфейсы (привет void *).


            1. Randl
              02.11.2015 08:55
              -1

              Массивы — низкоуровневая фича? А в каком языке из перечисленных (С++, C#, Java) их нет? Слайсы — опять-таки что в них низкоуровневого?
              Получаем язык высокого уровня с обработкой ошибок (и пустыми интерфейсами). И при том медленнее как С, так и С++.


        1. robert_ayrapetyan
          01.11.2015 21:25
          +1

          > Он идеально подходит как замена C
          Говоря о «замене С» подразумевается производительность. Можете привести какие-то бенчмарки? Иначе, на чем строиться ваше предположение? Из собственных наблюдений — Go в разы быстрее Python-а и т.п., но до производительности С ему очень далеко. Особенно когда в коде появляются го-рутины и каналы (а без них, на мой взгляд, использовать Go вообще нецелесообразно) — производительность сразу проседает в разы.


        1. nwalker
          02.11.2015 17:27
          +1

          offtop:
          JS — практичный?.. WAT.


  1. andrewnester
    01.11.2015 17:19
    +25

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


    1. Kolyuchkin
      01.11.2015 17:49
      +5

      +1


    1. neolink
      01.11.2015 18:02
      +6

      мне кажется плох тот инженер, который составил свое мнение на основе каких-то статей или (внезапно) комментариев, а не потому, что честно взял и сам попробовал.


      1. andrewnester
        01.11.2015 19:00
        +7

        ну тут спору нет, проблема в том, что само желание пробовать может пропасть


        1. Xlab
          02.11.2015 14:18

          Вам для мотивации нужно побольше комментов да лайков? Ну-ну =)


      1. salas
        05.11.2015 19:28
        -1

        А речь о молотке, которым лично инженер забивает гвоздь? Об экспериментальной реализации какой-нибудь новинки computer science? Или всё-таки о языке, на котором люди пишут? Причём пока тот язык незнакомый, легче оценить то, что те же люди пишут на других языках (в данном случае, на русском).


    1. divan0
      01.11.2015 18:05
      -2

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


      1. andrewnester
        01.11.2015 18:58
        +1

        Значит материал очень неоднозначный, переводили бы Effective Go тогда, пользы было бы больше


        1. divan0
          01.11.2015 19:48
          -5

          Материал этот полезный, и ценный. Уж фильтровать статьи для перевода по признаку «как на них отреагируют Go-хейтеры» я точно не буду.

          Effective Go не тот формат для Хабра.


          1. Kolyuchkin
            01.11.2015 21:45
            +4

            Я бы с удовольствием прочитал. Хоть и сам могу понять на оригинале, но интересует больше видение и вставки переводчика. Да и коментарии… Зачастую от них даже пользы больше)


      1. Randl
        02.11.2015 09:06
        +10

        Меня удивляет, что конструктивную, в общем-то, критику вы выставляете как неадекватные выкрики Go-хейтеров. И тонкие намеки в виде десятка минусов, вы, видимо, воспринимаете как гонения теми самыми хейтерами.
        Попробуйте честно и технически конструктивно отвечать на вопросы, даже если они выставляют Go не в лучшем свете, что в первом комментарии за вас более-менее сделал neolink
        Аргументы ad hominem тоже чести вам не делают.
        При этом перевод-то неплохой и исходная статья хоть и спорная, но интересная.
        ИМХО.


        1. divan0
          02.11.2015 09:58
          -5

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

          Но то, что мне не стоит влазить с ними в перепалки — соглашусь, конечно.


          1. Randl
            02.11.2015 10:20
            +8

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


  1. rekby
    01.11.2015 23:04
    +1

    А знает кто-то библиотеку для работы с ошибками — чтобы при создании ошибки у неё внутри сохранялся бы стектрейс и вложенная ошибка — чтобы потом можно было легко понять где она возникла даже если ошибка уже на несколько уровней вверх прокинута?


  1. oopcode
    02.11.2015 22:20
    +1

    Я тут недавно задавал вопрос на stackoverflow — по поводу того, почему компилятор Go не заставляет нас проверять ошибки (или хотя бы эксплицитно отказываться от их проверки).

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

    Вообще, конечно, я не уверен в том, что ошибки в Go — это удобно. Но пока живем, вроде.


    1. divan0
      02.11.2015 22:34

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