title


Буквально пару дней назад в Денвере закончилась очередная, уже 5-я по счёту, крупнейшая конференция по Go – GopherCon. На ней команда Go сделала важное заявление – черновики предварительного дизайна новой обработки ошибок и дженериков в Go 2 опубликованы, и все приглашаются к обсуждению.


Я постараюсь подробно пересказать суть этих черновиков в трёх статьях.


Как многим, наверняка, известно, в прошлом году (также на GopherCon) команда Go объявила, что собирает отчёты (experience reports) и предложения для решения главных проблем Go – тех моментов, которые по опросам собирали больше всего критики. В течении года все предложения и репорты изучались и рассматривались, и помогли в создании черновиков дизайна, о которых и будет идти речь.


Итак, начнём с черновиков нового механизма обработки ошибок.


Для начала, небольшое отступление:


  1. Go 2 это условное название – все нововведения будут частью обычного процесса выпуска версий Go. Так что пока неизвестно, будет ли это Go 1.34 или Go2. Сценария Python 2/3 не будет железно.
  2. Черновики дизайна это даже не предложения (proposals), с которых начинается любое изменение в библиотеке, тулинге или языке Go. Это начальная точка для обсуждения дизайна, предложенная командой Go после нескольких лет работы над данными вопросами. Всё, что описано в черновиках с большой долей вероятности будет изменено, и, при наилучших раскладах, воплотится в реальность только через несколько релизов (я даю ~2 года).

В чём проблема с обработкой ошибок в Go?


В Go изначально было принято решение использовать "явную" проверку ошибок, в противоположность популярной в других языках "неявной" проверке – исключениям. Проблема с неявной проверкой ошибок в том, как подробно описано в статье "Чище, элегантней и не корректней", что очень сложно визуально понять, правильно ли ведёт себя программа в случае тех или иных ошибок.


Возьмём пример гипотетического Go с исключениями:


func CopyFile(src, dst string) throws error {
    r := os.Open(src)
    defer r.Close()

    w := os.Create(dst)
    io.Copy(w, r)
    w.Close()
}

Это приятный, чистый и элегантный код. Он также некорректый: если io.Copy или w.Close завершатся неудачей, данный код не удалит созданный и недозаписанный файл.


С другой стороны, код на реальном Go выглядит так:


func CopyFile(src, dst string) error {
    r, err := os.Open(src)
    if err != nil {
        return err
    }
    defer r.Close()

    w, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer w.Close()

    if _, err := io.Copy(w, r); err != nil {
        return err
    }
    if err := w.Close(); err != nil {
        return err
    }
}

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


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


Проще говоря, в Go слишком много проверки ошибок и недостаточно обработки ошибок. Более полноценная версия кода выше будет выглядеть вот так:


func CopyFile(src, dst string) error {
    r, err := os.Open(src)
    if err != nil {
        return fmt.Errorf("copy %s %s: %v", src, dst, err)
    }
    defer r.Close()

    w, err := os.Create(dst)
    if err != nil {
        return fmt.Errorf("copy %s %s: %v", src, dst, err)
    }

    if _, err := io.Copy(w, r); err != nil {
        w.Close()
        os.Remove(dst)
        return fmt.Errorf("copy %s %s: %v", src, dst, err)
    }

    if err := w.Close(); err != nil {
        os.Remove(dst)
        return fmt.Errorf("copy %s %s: %v", src, dst, err)
    }
}

Исправление проблем сделало код корректным, но никак не чище или элегантней.


Цели


Команда Go ставит перед собой следующие цели для улучшения обработки ошибок в Go 2:


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

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


Дизайн


Предложенный дизайн вводит две новых синтаксические формы.


  • check(x,y,z) или check err обозначающую явную проверку ошибки
  • handle – определяющую код, обрабатывающий ошибки

Если check возвращает ошибку, то контроль передаётся в ближайший блок handle (который передаёт контроль в следущий по лексическому контексту handler, если такой есть, и. затем, вызывает return)


Код выше будет выглядеть так:


func CopyFile(src, dst string) error {
    handle err {
        return fmt.Errorf("copy %s %s: %v", src, dst, err)
    }

    r := check os.Open(src)
    defer r.Close()

    w := check os.Create(dst)
    handle err {
        w.Close()
        os.Remove(dst) // (только если check упадёт)
    }

    check io.Copy(w, r)
    check w.Close()
    return nil
}

Этот синтаксис разрешён также в функциях, которые не возвращают ошибки (например main). Следующая программа:


func main() {
    hex, err := ioutil.ReadAll(os.Stdin)
    if err != nil {
        log.Fatal(err)
    }

    data, err := parseHexdump(string(hex))
    if err != nil {
        log.Fatal(err)
    }

    os.Stdout.Write(data)
}

может быть переписана как:


func main() {
    handle err {
        log.Fatal(err)
    }

    hex := check ioutil.ReadAll(os.Stdin)
    data := check parseHexdump(string(hex))
    os.Stdout.Write(data)
}

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


func printSum(a, b string) error {
    x, err := strconv.Atoi(a)
    if err != nil {
        return err
    }
    y, err := strconv.Atoi(b)
    if err != nil {
        return err
    }
    fmt.Println("result:", x + y)
    return nil
}

может быть переписан как:


func printSum(a, b string) error {
    handle err { return err }
    x := check strconv.Atoi(a)
    y := check strconv.Atoi(b)
    fmt.Println("result:", x + y)
    return nil
}

или даже вот так:


func printSum(a, b string) error {
    handle err { return err }
    fmt.Println("result:", check strconv.Atoi(x) + check strconv.Atoi(y))
    return nil
}

Давайте рассмотрим подробнее детали предложенных конструкций check и handle.


Check


check это (скорее всего) ключевое слово, которое чётко выражает действие "проверка" и применяется либо к переменной типа error, либо к функции, возвращающую ошибку последним значением. Если ошибка не равна nil, то check вызывает ближайший обработчик(handler), и вызывает return с результатом обработчика.


Следующий пример:


v1, ..., vN := check <выражение>

равнозначен этому коду:


v1, ..., vN, vErr := <выражение>
if vErr != nil {
    <error result> = handlerChain(vn)
    return
}

где vErr должен иметь тип error и <error result> означает ошибку, возвращённую из обработчика.


Аналогично,


foo(check <выражение>)

равнозначно:


v1, ..., vN, vErr := <выражение>
if vErr != nil {
    <error result> = handlerChain(vn)
    return
}
foo(v1, ..., vN)

Check против try


Изначально пробовали слово try вместо check – оно более популярное/знакомое, и, например, Rust и Swift используют try (хотя Rust уходит в пользу постфикса ? уже).


try неплохо читался с функциями:


data := try parseHexdump(string(hex))

но совершенно не читался со значениями ошибок:


data, err := parseHexdump(string(hex))
if err == ErrBadHex {
    ... special handling ...
}
try err

Кроме того, try всё таки несёт багаж cхожести с механизмом исключений и может вводить в заблуждение. Поскольку предложенный дизайн check/handle существенно отличается от исключений, выбор явного и красноречивого слова check кажется оптимальным.


Handle


handle описывает блок, называемый "обработчик" (handler), который будет обрабатывать ошибку, переданную в check. Возврат (return) из этого блока означает незамедлительный выход из функции с текущими значениями возвращаемых переменных. Возврат без переменных (то есть, просто return) возможен только в функциях с именованными переменными возврата (например func foo() (bar int, err error)).


Поскольку обработчиков может быть несколько, формально вводится понятие "цепочки обработчиков" – каждый из них это, по сути, функция, которая принимает на вход переменную типа error и возвращает те же самые переменные, что и функция, для которой обработчик определяется. Но семантика обработчика может быть описана вот так:


func handler(err error) error {...}

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


Порядок обработчиков


Важный момент для понимания – в каком порядке будут вызываться обработчики, если их несколько. Каждая проверка (check) может иметь разные обработчики, в зависимости от скопа, в котором они вызываются. Первым будет вызван обработчик, который ближе всего объявлен в текущем скопе, вторым – следующий в обратном порядке объявления. Вот пример для лучшего понимания:


func process(user string, files chan string) (n int, err error) {
    handle err { return 0, fmt.Errorf("process: %v", err)  }      // handler A
    for i := 0; i < 3; i++ {
        handle err { err = fmt.Errorf("attempt %d: %v", i, err) } // handler B
        handle err { err = moreWrapping(err) }                    // handler C

        check do(something())  // check 1: handler chain C, B, A
    }
    check do(somethingElse())  // check 2: handler chain A
}

Проверка check 1 вызовет обработчики C, B и A – именно в таком порядке. Проверка check 2 вызовет только A, так как C и B были определены только для скопа for-цикла.


Конечно, в данном дизайне сохраняется изначальный подход к ошибкам как к обычным значениям. Вы всё также вольны использовать обычный if для проверки ошибки, а в обработчике ошибок (handle) можно (и нужно) делать то, что наилучшим образом подходит ситуации – например, дополнять ошибку деталями перед тем, как обработать в другом обработчике:


type Error struct {
    Func string
    User string
    Path string
    Err  error
}

func (e *Error) Error() string

func ProcessFiles(user string, files chan string) error {
    e := Error{ Func: "ProcessFile", User: user}
    handle err { e.Err = err; return &e } // handler A
    u := check OpenUserInfo(user)         // check 1
    defer u.Close()
    for file := range files {
        handle err { e.Path = file }       // handler B
        check process(check os.Open(file)) // check 2
    }
    ...
}

Стоит отметить, что handle несколько напоминает defer, и можно решить, что порядок вызова будет аналогичным, но это не так. Эта разница – одна из слабых место данного дизайна, кстати. Кроме того, handler B будет исполнен только раз – аналогичный вызов defer в том же месте, привёл бы ко множественным вызовам. Go команда пыталась найти способ унифицировать defer/panic и handle/check механизмы, но не нашла разумного варианта, который бы не делал язык обратно-несовместимым.


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


Паника (panic) в обработчиках исполняется так же, как и в теле функции.


Обработчик по-умолчанию


Ещё одна ошибка компиляции – если код обработчика пуст (handle err {}). Вместо этого вводится понятие "обработчика по-умолчанию" (default handler). Если не определять никакой handle блок, то, по-умолчанию, будет возвращаться та же самая ошибка, которую получил check и остальные переменные без изменений (в именованных возвратных значениях; в неименованных будут возвращаться нулевые значения — zero values).


Пример кода с обработчиком по-умолчанию:


func printSum(a, b string) error {
    x := check strconv.Atoi(a)
    y := check strconv.Atoi(b)
    fmt.Println("result:", x + y)
    return nil
}

Сохранение стека вызова


Для корректных стектрейсов Go трактует обработчики как код, вызывающийся из функции в своем собственном стеке. Нужен будет какой-то механизм, позволяющий игнорировать код обработчика в стектрейсе, например для табличных тестов. Скорее всего, вот использование t.Helper() будет достаточно, но это ещё открытый вопрос:


func TestFoo(t *testing.T) {
    handle err {
        t.Helper()
        t.Fatal(err)
    }
    for _, tc := range testCases {
        x := check Foo(tc.a)
        y := check Foo(tc.b)
        if x != y {
            t.Errorf("Foo(%v) != Foo(%v)", tc.a, tc.b)
        }
    }
}

Затенение (shadowing) переменных


Использование check может практически убрать надобность в переопределении переменных в краткой форме присваивания (:=), поскольку это было продиктовано именно необходимостью переиспользовать err. С новым механизмом handle/check затенение переменных может вообще стать неактуальным.


Открытые вопросы


defer/panic


Использование похожих концепций (defer/panic и handle/check) увеличивает когнитивную нагрузку на программиста и сложность языка. Не очень очевидные различия между ними открывают двери для нового класса ошибок и неправильного использования обоих механизмов.


Поскольку handle всегда вызывается раньше defer (и, напомню, паника в коде обработчика обрабатывается так же, как и в обычном теле функции), то нет способа использовать handle/check в теле defer-а. Вот этот код не скомпилируется:


func Greet(w io.WriteCloser) error {
    defer func() {
        check w.Close()
    }()
    fmt.Fprintf(w, "hello, world\n")
    return nil
}

Пока не ясно, как можно красиво решить эту ситуацию.


Уменьшение локальности кода


Одним из главных преимуществ нынешнего механизма обработки ошибок в Go является высокая локальность – код обработчика ошибки находится очень близко к коду получения ошибки, и исполняется в том же контексте. Новый же механизм вводит контекстно-зависимый "прыжок", похожий одновременно на исключения, на defer, на break и на goto. И хотя данный подход сильно отличается от исключений, и больше похож на goto, это всё ещё одна концепция, которую программисты должны будут учить и держать в голове.


Имена ключевых слов


Рассматривалось использование таких слов как try, catch, ? и других, потенциально более знакомых из других языков. После экспериментирования со всеми, авторы Go считают, что check и handle лучше всего вписываются в концепцию и уменьшают вероятность неверного трактования.


Что делать с кодом, в котором имена handle и catch уже определены, пока тоже не ясно (не факт, что это будут ключевые слова (keywords) ещё).


Часто задаваемые вопросы


Когда выйдет Go2?


Неизвестно. Учитывая прошлый опыт нововведений в Go, от стадии обсуждения до первого экспериментального использования проходит 2-3 релиза, а официальное введение – ещё через релиз. Если отталкиваться от этого, то это 2-3 года при наилучших раскладах.


Плюс, не факт, что это будет Go2 – это вопрос брендинга. Скорее всего, будет обычный релиз очередной версии Go – Go 1.20 например. Никто не знает.


Разве это не то же самое, что исключения?


Нет. В исключениях главная проблема в неявности/невидимости кода и процесса обработки ошибок. Данный дизайн лишен такого недостатка, и является, фактически, синтаксическим сахаром для обычной проверки ошибок в Go.


Не разделит ли это Go программистов на 2 лагеря – тех, кто останется верным if err != nil {} проверкам, и сторонников handle/check?


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


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


Является. Расчёт на то, что выгода от этого усложнения перевесит минусы самого факта усложнения.


Это окончательный дизайн и точно ли он будет принят?


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


Я знаю, как сделать дизайн лучше! Что мне делать?


Напишите статью с объяснением вашего видения и добавьте её в вики-страничку Go2ErrorHandlingFeedback


Резюме


  • Предложен новый механизм обработки ошибок в будущих версиях Go —  handle/check
  • Обратно-совместим с нынешним
  • Проверка и обработка ошибок остаются явными
  • Сокращается количество текста, особенно в кусках кода, где много повторений однотипных ошибок
  • В грамматику языка добавляются два новых элемента
  • Есть открытые/нерешённые вопросы (взаимодействие с defer/panic)

Ссылки



Мысли? Комментарии?


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


  1. youROCK
    02.09.2018 20:47
    +2

    Вся эта семантика явно делает го сложнее для чтения и написания, но решение давно просилось, и в принципе выглядит не так уж плохо (но и не сказать, чтобы прямо супер очевидно) :). Что кто думает?


  1. vintage
    02.09.2018 20:58
    +3

    handle err {
            w.Close()
    }
    
    check w.Close()

    Ммм, это пять :-)


    func printSum(a, b string) error {
        x := check strconv.Atoi(a)
        y := check strconv.Atoi(b)
        check fmt.Println("result:", x + y)
        return nil
    }

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


    1. Edison
      02.09.2018 22:53

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

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


      1. vintage
        03.09.2018 00:04
        -1

        Ну суть как раз в явности — видно какая функция возвращает ошибку.

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


        Все явно — где и как ошибка обработана.

        Куда важнее где и какая ошибка не обработана и на этот вопрос Go не отвечает. Программист не может понять какие вообще типы ошибок могут прилететь. Компилятор это тоже не проверяет.


        1. divan0 Автор
          03.09.2018 00:25
          +1

          Да все функции возвращают ошибку.

          Это неверное утверждение.
          Быстрый поиск по стандартной библиотеке:
          $ grep -r '^func' . | grep error | wc -l
          12928
          $ grep -r '^func' . | grep -v error | wc -l
          38878


          Куда важнее где и какая ошибка не обработана и на этот вопрос Go не отвечает.

          Статический анализ не может дать ответ во всех возможных случаях, нужно ли проверять ошибку или нет. Например, `fmt.Println()` возвращает ошибку, но в большинстве случаев практического смысла проверять её нет. Даже линтер `errcheck` тут не будет ругаться.


          1. dblokhin
            03.09.2018 04:25

            $ grep -r '^func'. | grep error | wc -l
            12928
            $ grep -r '^func'. | grep -v error | wc -l
            38878

            Это неверный подход к анализу. Из выборки также необходимо исключить те функции, которые ничего не возвращают (процедуры, которые не задеклалированы в языке). А почти треть всех функций с error все же значимая часть кода, несмотря на пример с `fmt.Println()`, который больше исключение подтверждающий правило.

            В целом направление интересное и возможно верное, хотя check станет самым частым словом в теле функции как в примере выше. Хоть и укорачивает, но слегка нечитабельно.
            Возможно, как вариант вообще избавиться от слова check, введя дополнительный вид присваивания? Например, так:
            func printSum(a, b string) error {
                x ~ strconv.Atoi(a)
                y ~ strconv.Atoi(b)
                check fmt.Println("result:", x + y)
                return nil
            }

            Где `~` есть эквивалент `:= check`. Или вот так, получше вариант:
            func printSum(a, b string) error {
                @x := strconv.Atoi(a)
                @y := strconv.Atoi(b)
                check fmt.Println("result:", x + y)
                return nil
            }


            1. Deosis
              03.09.2018 07:05

              Последний пример напомнил перл с его:


              @file = open($file) or die "cant open file $file: $!"


            1. divan0 Автор
              03.09.2018 09:44

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

              Насчёт односимвольных синтаксических конструкций – в Go они никогда не приживались, и, надеюсь, и не будут. `check` несёт смысл в самом названии и, даже без чтения спецификации/книжек можно догадаться о его функции, а вот `~` и `@` уже никак не догадаешься сходу.


            1. diafour
              03.09.2018 14:34

              @ и ошибки это из php, там правда смысл несколько иной: http://php.net/manual/ru/language.operators.errorcontrol.php


          1. vintage
            04.09.2018 08:15
            +2

            Ой как весело комментировать только половину фразы.


            А как вы понимаете надо ли обрабатывать ошибку от fmt.Println()? Мне вот ни из сигнатуры, ни из документации это не понятно:


            func Println(a ...interface{}) (n int, err error)
            Println formats using the default formats for its operands and writes to standard output. Spaces are always added between operands and a newline is appended. It returns the number of bytes written and any write error encountered.

            Что за ошибки? В каких случаях возникают? Насколько критичные?


            fmt.Println("result:", x + y)

            Функция не возвращает ошибок? Или возвращает, но мы их игнорируем? Или мы забыли обработать ошибки? Или мы не знали, что функция вообще возвращает ошибки? Или знали лишь о 3 из 5 вариантах падения и не были в курсе, что оставшиеся 2 надо обработать по особому?


            1. divan0 Автор
              04.09.2018 09:40
              -3

              А как вы понимаете надо ли обрабатывать ошибку от fmt.Println()? Мне вот ни из сигнатуры, ни из документации это не понятно.


              В комментарии сказано «write error». Также в нём сказано, что запись производится в stdout. Этого достаточно, чтобы сообразить, что возможна ошибка записи в stdout и уже вам судить, насколько это вероятная ситуация и что в ней делать.

              А про «половину фразы» не надо – ради краткости есть смысл не копировать все предложения, которые не меняют смысл вашего утверждения про «все функции возвращают ошибку».


              1. ZyXI
                04.09.2018 13:28
                -1

                И какая же «ошибка записи» возможна? man 3p write, раздел ERRORS описывает 23 возможных ошибки, хотя часть с одними кодами, часть применима только к write() или только к pwrite() (кстати, ещё могли сообразить обёртку над writev(), в определённых случаях функция более удобная, тем более если нужно писать более одной строки), и всё применимо только к linux. Какие из этих ошибок может вернуть fmt.Println, а какие либо не возникнут вообще, либо будут обработаны самой fmt.Println и не дойдут до пользователя (я про EINTR в первую очередь, пользователя обёрток практически никогда не заморачивают обработкой этой ошибки)? Что она вообще вернёт конкретно и будет ли она возвращать одинаковой результат для схожих ошибок на Windows и linux?


                1. divan0 Автор
                  04.09.2018 16:27

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

                  В вышеприведённом примере понятно, что Println может вернуть ошибку записи в stdout. Этого в 90% случаев достаточно, чтобы понять, как реагировать на данную ошибку. Работа со стандартными дескрипторами в posix это знакомая концепция, поэтому легко сделать вывод, что если произошла ошибка записи в stdout, то программа мало что может тут сделать. Поэтому в подавляющем большинстве практических случаев совершенно не важно, какой из 23 ошибок может быть возвращаемая ошибка в Println, и, учитывая крайнюю редкость ситуации, её есть смысл игнорировать.

                  Это называется практический опыт и здравый смысл. Если вам нужно копать глубже и для вашей программы действительно важно среагировать на закрытый дескриптор иначе, чем на отсутствие памяти – простыми движениями вы сможете эту информацию найти, спасибо godef и gocode. В Go почти везде используется оптимизация по принципу Парето – дефолтные значения и дизайн заточен под 80% случаев, для остальных 20% есть способы копнуть глубже.


  1. Shtucer
    02.09.2018 22:06

    del


  1. tirinox
    02.09.2018 22:11
    +1

    On Error Resume Next


    1. andy128k
      02.09.2018 22:15
      +3

      Больше похоже на ON ERROR GOSUB.


  1. ageres
    02.09.2018 23:28
    +5

    catch (error) {
    ...handle error...
    } try {
    ... do something...
    }

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


    1. creker
      03.09.2018 01:43
      -1

      Исключения мало что общего имеют с этим. Явная проверка ошибок, отсутствие stack unwinding. Роднит лишь прыжок в общий блок обработки. В Go просился синтаксический сахар и это один из его вариантов. Наиболее близкий аналог это ошибки в Swift, где, несмотря на try/catch, с исключениями тоже ничего общего.


      1. ageres
        03.09.2018 01:59

        Абсолютно согласен, это совсем не исключения.


  1. KirEv
    03.09.2018 01:16

    Мне, как разработчику на go, очень не хватает како-то модификатора функции, или макроса, например, чтобы:

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

    2. на этапе компиляции указывать компилятору закрывать все открытые ресурсы после завершения функции

    под завершением работы функции имею ввиду любой завершение, паник, корректное (или возврат ошибекИ)


    1. youROCK
      03.09.2018 01:20

      Вроде defer должен решить вашу проблему :)


    1. divan0 Автор
      03.09.2018 09:45

      посмотрите на runtime.SetFinalizer


      1. ainar-g
        04.09.2018 17:24

        Финализаторы вроде как крайне не рекомендуют. Тут про C#, но многое применимо и к Go.

        Я бы использовал defer, как рекомендует коллега выше.


  1. nikitadanilov
    03.09.2018 01:19

    handle err {… } это возрожденный ON-unit из PL/I? Казалось бы отцы-основатели структурного программирования высмеяли такие штуки навсегда, но нет.


  1. vsb
    03.09.2018 02:22
    +2

    Хоть я и являюсь сторонником исключений и считаю их самым удобным способом обработки ошибок, которое придумало человечество, но в принципе этот сахарок пожалуй достаточно приятен, чтобы Go для меня стал юзабелен. Ещё бы стектрейсы в стандартные ошибки добавили и удобный chaining, использовал pkg.errors для этого, но это должно быть в стандартной библиотеке, это база.


    1. creker
      03.09.2018 02:31

      Расширить функциональность ошибок тоже входит в их планы и описано в черновике по-соседству


      1. divan0 Автор
        03.09.2018 11:02

        Да, это будет в следующей статье.


    1. 0xd34df00d
      03.09.2018 19:01
      +1

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

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


      1. Sirikid
        03.09.2018 19:41

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


        1. 0xd34df00d
          03.09.2018 19:56

          Проверяемые исключения — скорее сахарок над чем-то вроде Either (Ex1 :+: Ex2 :+: ... :+: ExN) Result, по крайней мере, такой взгляд на них, ИМХО, консистентнее.


          Но это уже больше вопрос терминологии.


          1. Sirikid
            03.09.2018 20:09

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


  1. varanio
    03.09.2018 06:41
    +1

    От слова check рябит в глазах


  1. Gizmich
    03.09.2018 08:29

    Так что пока неизвестно, будет ли это Go 1.34 или Go2. Сценария Python 2/3 не будет железно.

    Это значит что они планируют сохранить обратную совместимость?


    1. divan0 Автор
      03.09.2018 09:47

      Да, это приоритет номер 1 во всех этих изменениях.


  1. tgz
    03.09.2018 08:47

    Расскажите им про монады что ли…


    1. 0xd34df00d
      03.09.2018 18:58

      Для этого дженерики нужны, а мы все много раз слышали, что в Go они не нужны.

      А, стоп, в Go 2 будет? Как же так?


      1. creker
        04.09.2018 00:12

        Толстенько. Они действительно не особо нужны, когда не теории на бумаге рисуешь, а код пишешь. Go дает все необходимое из коробки и спокойно так десять лет прожил, отвоевав себе приличный кусок рынка. Авторы языка самой идее никогда не противились и с самого начала хотели добавить, но должной реализации не было. Сейчас есть куча разных предложений, но все имеют свои плюсы и минусы. Поэтому вопрос дженериков опять поднят прямо как при создании Go 1, но они могут и не появиться. Авторы и комьюнити не находятся в отчаянном положении, лишь бы добавить какую-то реализацию. Ничего не понравится — ничего не добавят. Может зато потратят время на другую идею, т.к. в Go 2 авторы менять сразу много тоже не хотят. 2-3 крупных изменения, а изменений на предлагали полно.


        1. Edison
          04.09.2018 00:23

          Ну даже написали почему не добавили generics — golang.org/doc/faq#generics
          Ниже там и про exceptions, assertions, etc.


          1. creker
            04.09.2018 00:26

            Дак кто ж это читает. Язык не интересен настолько, чтобы разобраться, но настолько, чтобы сраться в комментах. Ситуация знакомая, так что лицемерить и не буду даже. По-хорошему, авторы дали ответ наверное на все претензии к языку. Особенно почему нет той или иной фичи из языка Х


        1. SirEdvin
          05.09.2018 15:44

          И списками вы, наверное, тоже не пользуетесь. Не нужны ведь.


        1. 0xd34df00d
          05.09.2018 17:51

          Видимо, я код не пишу. Или он на самом деле на бумаге рисуется, не знаю.


          Увы, у меня сходу не получилось спросить у гитхаба, в скольких строках кода встречается interface{} (он почему-то скобочки игнорирует) — а это хороший кандидат на первое приближение к тому, насколько востребованы дженерики.


  1. eao197
    03.09.2018 09:44
    +2

    По примерам в статье складывается ощущение, что вместо check/handle достаточно было повторить для Go-шного defer идею D-шного scope(exit/success/failure). Т.е. чтобы можно было писать defer(success) и defer(failure) в дополнение к нынешнему defer. Тогда можно было бы продолжать использовать существующий механизм обработки ошибок, но при этом делать какие-то специфические действия при выходе из-за ошибки (удалять ненужные файлы, например).

    func CopyFile(src, dst string) error {
        r, err := os.Open(src)
        if err != nil {
            return fmt.Errorf("copy %s %s: %v", src, dst, err)
        }
        defer r.Close()
    
        w, err := os.Create(dst)
        if err != nil {
            return fmt.Errorf("copy %s %s: %v", src, dst, err)
        }
    
        defer(failure) os.Remove(dst);
    
        if _, err := io.Copy(w, r); err != nil {
            w.Close()
            return fmt.Errorf("copy %s %s: %v", src, dst, err)
        }
    
        if err := w.Close(); err != nil {
            return fmt.Errorf("copy %s %s: %v", src, dst, err)
        }
    }


    1. divan0 Автор
      03.09.2018 11:02

    1. creker
      03.09.2018 11:11

      продолжать использовать существующий механизм обработки ошибок

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


      1. eao197
        03.09.2018 11:50

        В текущем механизме обработки ошибок в Go со стороны видится две проблемы*:

        1. Многословность проверок кодов ошибок.

        2. Сложность с написанием кода по очистке ресурсов и/или откату каких-то операций при обработке ошибок.

        Первая проблема может быть решена тем или иным синтаксическим сахаром. Например, использованием ключевого слова check или префикса/суффикса '?'. Вроде такого:

        w := check os.Create(dst)
        // или
        w := ?os.Create(dst)
        


        Вторая проблема решается за счет defer(success/failure).

        * По поводу «проблем». ИМХО, своим успехом Go обязан именно такому вот примитивному и прямолинейному коду, который не требует высокой квалификации от разработчиков и делает код разных людей максимально однотипным. Так что, что кажется проблемой, ИМХО, на самом деле является одной из важнейших составляющих привлекательности Go в современном мире.


        1. creker
          03.09.2018 18:07

          Сложность с написанием кода по очистке ресурсов и/или откату каких-то операций при обработке ошибок

          Если честно, эту проблему я слышу сейчас первый раз. Я давно слежу и участвую в дискуссиях по поводу ошибок в Go 2, но ниразу я не встречал именно эту проблему. Да и на практике тоже.


          1. eao197
            03.09.2018 18:09

            Если честно, эту проблему я слышу сейчас первый раз.
            Значит вы не читали обсуждаемую статью, ибо в разделе «В чём проблема с обработкой ошибок в Go?» об этом и идет речь. См. пример из этого раздела, в котором os.Remove() нужно вызывать в двух if-ах. Это оно и есть.


            1. creker
              03.09.2018 18:19

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

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


              1. eao197
                03.09.2018 18:52

                Я даже на практике не помню проблем подобных. Удалить файл при ошибке — ерунда, это не требует изменений в языке. В том же самом defer можно банально проверить наличие ошибки и выполнить нужный код.
                Ну значит вообще весь этот огород с handle err не нужен, если по вашей практике судить. Достаточно лишь ввести конструкцию, которая будет аналогом Rust-овских try! и '?'. Делов-то.

                Вы это Google-овцам расскажите, а то они не знают, что на практике проблем нет, выдумывают какие-то handler-ы.


    1. el777
      04.09.2018 18:00

      А defer(failure) можно реализовать через проверку:


      defer func() {
          if err != nil {
             ...
          }
      }


  1. adjachenko
    03.09.2018 09:48
    +1

    Я пишу на с++, на го не пишу, но периодически почитываю статьи про другие языки, особенно про такую важную тему как обрабтока ошибок. Мне это новая обработка ошибок на го очень сильно напоминает с++ on_scope_exit концепцию/семейство функций. Ваш хендл это on_exception ваш дефер это on_scope_exit. Осталось добавить on_success аналог дефер который вызывается только если не было ошибок и относиться к проверяемым ошибкам как к эксепциям т.е. если функция вернула ошибку и она была вызвана с чеком то вызывать все подряд (дефер/хэндл) в порядке обратном объявлению. Дефер и хендл суть одно и тоже только дефер более общий случай, вызывается всегда неважно есть ошибка или нет. Нет смысла городить отдельные (от дефер) правила порядка вызовов для хендл.

    Кому интересны подробности из первоисточника и кто не знаком с с++ и/или концепций on_scope_exit гуглите по Андрей Александреску с++ декларативное программирование. Я смотрел/читал все на английском поэтому не уверен, что мой русский перевод нагуглиться. Вот как раз местный пост кому лень гуглить.


    1. divan0 Автор
      03.09.2018 09:50

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


  1. khrundel
    03.09.2018 09:50
    +13

    Теперь в самом простом языке 3 разных вида обработки ошибок, ни один из которых не является универсальным. Defer будет использоваться в случае, когда действие нужно выполнить вне зависимости от успешности (аналог finally), handle — когда приборка отличается в случае успеха и неудачи. Ну и старая добрая проверка err на nil никуда не денется, так как check подразумевает не обработку ошибки, а проброс вверх по стеку, если некоторая ошибка восстановима, то check не подходит. Особую прелесть добавляет факт, что defer и handle похожи, но заметно отличаются, один дружит с паникой, другой — нет, разно ведут себя в циклах, поэтому совершенно непонятно, автор использовал defer потому, что ждёт панику или просто потому что так писать короче.
    Кроме того, в любой более-менее серьёзной функции ошибка будет одной из множества, и они будут обрабатываться по-разному, файл не найден, тогда пробуем по другому пути или создаём, или файл создан в более новой версии и нужно отказаться трогать чтоб не испортить данные. Соответственно нужно писать ручную проверку типа ошибки. Ну вот написали мы проверку, как быть в ветке с необрабатываемой ошибкой? Просто вернуть err? Тогда не вызовутся уже установленные хэндлеры. Выполнить check err? Отличная идея, так и нужно делать… Вот только нам же обещали отсутствие ситуации питон 2 vs 3, код должен быть совместимым, добавляя свой хендлер в начале функции нужно сначала посмотреть, нет ли где в теле выхода с ненулевым err, а то хендлер проигнорируется.


    В общем этот го не получился. Выбросите и сделайте новый.


  1. claygod
    03.09.2018 10:08

    Если говорить в множестве вот таких кусков:

        r, err := os.Open(src)
        if err != nil {
            return err
        }

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

     r, err := os.Open(src) try ("blah-blah: %v", err)

    Вместо «try» поставьте любое красивое и подходящее слово.

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

    Ещё более коротко (без добавления некоей обработки ошибки):

     r, err := os.Open(src) try


    1. divan0 Автор
      03.09.2018 10:40
      +2

      Есть реальная возможность повлиять на дизайн:
      github.com/golang/go/wiki/Go2ErrorHandlingFeedback

      Хотя уже было похожее предложение: github.com/golang/go/issues/21161


      1. rustler2000
        03.09.2018 18:07

        Есть реальная возможность повлиять на дизайн:


        Вы, предположу, не подписаны на кейсы по обработкам ошибок и генерикам.
        Гугл в очередной раз всех поимел сделал все по своему (как с vgo vs dep).
        Ну и удивительно, что они выбрали такой фидбэк от коммьюнити а не вообще обычную почту.


        1. divan0 Автор
          03.09.2018 20:08
          +1

          Что, простите?


          1. rustler2000
            03.09.2018 22:44

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


            1. Edison
              03.09.2018 22:54
              +1

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


              1. rustler2000
                03.09.2018 23:32
                -1

                И это ничего не меняет.
                Гугл имеет свой интерес и профит и все решения принимает сам. Да они скорее всего пистонов отхватили за то как vgo выкатили, и теперь все могут аж напихать ссылок. Они даже в оригинальных кейсах линки на пропозал не воткунли. Wtf?


                1. divan0 Автор
                  04.09.2018 00:06

                  Ухты, conspiracy theories у нас ещё не было. Продолжайте)


                  1. rustler2000
                    04.09.2018 08:46

                    Гитхаб issue не место для дискуссий — github.com/golang/go/issues/21161#issuecomment-417154441


                1. creker
                  04.09.2018 00:14

                  Давайте я вам про type aliases еще напомню. Вот где был заговор всемирный.


                  1. rustler2000
                    04.09.2018 08:39

                    Хахаха — ты про эти два кейса? github.com/golang/go/issues/16339
                    github.com/golang/go/issues/18130 — так это только подтверждение что фидбэк им только мешает — изза публичной дискуссии они релиз пропустили.


                    1. divan0 Автор
                      04.09.2018 09:44
                      -1

                      Тоесть вы сейчас выискиваете по issues все случаи, когда Go team не согласился с предложенным изменением языка, и этим доказываете свою теорию заговора? Ок.


                      1. rustler2000
                        04.09.2018 10:41

                        Wat?!
                        Тоесть эти два «не связанные» пропосала внесла не го тим и даже не гуглеры?!
                        И фича гуглу не нужна была вовсе?
                        И релиз они не пропустили?
                        А форсить пользователей постить линки на вики страничке (где подписаться на изменения нельзя) это несомненно улучшение процесса?!


                        1. divan0 Автор
                          04.09.2018 11:10

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


            1. divan0 Автор
              04.09.2018 00:05

              Я понял ваше мнение, но хорошо зная многих людей и из Go команды и из коммьюнити, я не вижу ни единого подтверждения вашим словам. История vgo vs dep, которую вы так удобненько пытаетесь представить в виде доказательства, совершенно тут не к месту, в ней своя история и свои проблемы, и мы можем на эту тему пообщаться, если хотите.
              Хотите верить, что давать конструктивный фидбек, когда его просят – бесполезно: пожалуйста. Только не подавайте это как какой-то факт, и уж тем более, не притягивайте за уши аргументы, о которых другие люди более осведомлены.


              1. rustler2000
                04.09.2018 09:15

                Какие именно аргументы притянуты за уши?
                Что через «публичные» обсуждения их пропосалы тяжело проходят?
                Что вместо осуждения их пропосала через issue они услажняют процесс обсуждения?

                Again, I apologize for all the mistakes I made during this process. I've learned a lot about what a successful community collaboration does and does not look like, and I hope it helps the next one run more smoothly.


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


                1. divan0 Автор
                  04.09.2018 09:45
                  -1

                  Что вместо осуждения их пропосала через issue они услажняют процесс обсуждения?

                  А как надо обсуждать? Если у вас больше опыта в развитии нового языка и коммьюнити, напишите как нужно – если ваш подход будет лучше, то все прислушаются.=


  1. infrapro
    03.09.2018 10:24
    +1

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


    1. divan0 Автор
      03.09.2018 10:37
      +2

      Так предлагайте. Есть реальный шанс повлиять на дизайн же.
      github.com/golang/go/wiki/Go2ErrorHandlingFeedback

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


      1. infrapro
        03.09.2018 10:59
        +1

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


  1. crash_ntech
    03.09.2018 10:34
    -1

    if result, err = funcName(); err == nil {
    //DO SONTHING
    } else {
    fmt.Println("Error:", err)
    }


  1. fukkit
    03.09.2018 10:54
    -2

    Так и не пойму, зачем мучить хомяка. Ну не завезли нормальных исключений изначально — бывает, так ведь дело поправимое.
    Легаси библиотеки никому переписывать не хочется — это понятно. Можно было бы добавлять «check» или что-то подобное перед функциями, ошибки которых хочется выбрасывать исключениями, а дальше: try, catch, finally — все как у людей.


    1. CuredPlumbum
      05.09.2018 16:43

      «Не завезли» исключения по причинам уже озвучинными авторами: golang.org/doc/faq#exceptions. И check/handle вовсе не возврат к исключениям.


      1. fukkit
        05.09.2018 17:23
        +2

        Краткий перевод:
        «Нам кажется, что правильное использование проверенной временем конструкции „try-catch-finally“ наш бородатый контингент не осилит и наговнокодит на Гошке так, что все шишки опять полетят дизайнерам языка (а далеко не все из шишек нам по вкусу!).

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

        Мы пойдем своим путём. Для обработки обычных ошибок, пожалуйста, используйте возможность возврата множества значений из одной функции (заодно подразобьёте все свои if'ы на присвоение/проверку, Гошка проще всех, вы не знали?).

        Встроенный тип „ошибка“ и другие запиленные нами удобства позволят вам обрабатывать все мыслимые ошибки, шутя и наслаждаясь. Однако мы постарались, чтобы имеющийся опыт в других языках здесь вам ни разу не пригодился (разве что в Си или Бэйсике).

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

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

        Великолепие Гошки в обработке ошибок вы в полной мере оцените после пары-тройки серьезных проектов (*демонический смех)

        Вкусного вам кофе, держитесь там,
        любящий вас в мыслях
        авторский коллектив.»


        1. CuredPlumbum
          05.09.2018 17:53

          Ну, перевод немного странный. А вы, стесняюсь спросить, в каком языке программирования практикуете?


          1. 0xd34df00d
            05.09.2018 17:57

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


            Особенно после современных плюсов, где вполне легко делается аналог Either.


  1. SirEdvin
    03.09.2018 11:03
    +3

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


    Ну и как обычно


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

    Нет, это не проблема исключений, правда. В java такой проблемы, например, нет и всегда понятно, какое исключение может выбросить функция (исключая Runtime, но и panic в go есть).


    А любителям рассказывать, что вот для java нужно ide, а для golang — нет, можно я напомню, что у go с этим делом еще хуже в силу того, что существует неявный импорт всех модулей из папки и в целом импорт сразу всего, а не по деталям в моде. Из интерфейса github код на go очень больно читается из-за этого.


    1. SirEdvin
      03.09.2018 11:08

      Ну и как уже отметили — очень круто, что наконец-то начали думать про лишний текст)


    1. divan0 Автор
      03.09.2018 11:12
      -4

      Теперь то можно встать в полный рост и сказать «а я же говорил».

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


      1. SirEdvin
        03.09.2018 11:15

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

        А как там такой же репорт по поводу дженериков практически на заре go? Или я что-то путаю?


        1. divan0 Автор
          03.09.2018 11:16
          -1

          Не очень понял вопрос.


          1. SirEdvin
            03.09.2018 11:18

            Если я не ошибаюсь, где-то для версии go1.5,go1.6 кто-то написал очень хороший propasal по дженерикам, на что был большой ответ в google docs где подробно объяснялось почему "это не нужно".


            Или я что-то путаю?


            1. divan0 Автор
              03.09.2018 12:09
              -1

              Первый раз слышу. Ссылки?


              1. SirEdvin
                03.09.2018 12:23

                Если я не ошибаюсь, вот этот документ. Вроде как он появился раньше 2016 года (если судить по issue и я вполне могу ошибаться). Довольно подробный документ по generiс в целом.


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


                1. divan0 Автор
                  03.09.2018 12:28

                  Вы, похоже, не читали документ. Он даже упоминается в новом драфте по дизайну. Это просто обзор проблемы, с доступным описанием и обзором реализаций. Я его сам не раз тут постил.

                  Каким образом вы его трансформировали в «где-то для версии go1.5,go1.6 кто-то написал очень хороший propasal по дженерикам, на что был большой ответ в google docs где подробно объяснялось почему „это не нужно“.» – для меня загадка, но это очень хорошо иллюстрирует суть всех дискуссий с Вами.


      1. anjensan
        03.09.2018 11:29
        +7

        Простите, но давайте сравним.

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

        По итогу комьюнити/коре-тим таки пришло к выводу, что проблема есть. Кто сделал полезное дело, SirEdvin, призывающий решить проблему, или divan0, закрывающий на нее глаза?


        1. divan0 Автор
          03.09.2018 12:25
          -1

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

          Я ваши комментарии тоже помню, поэтому не ожидаю объективности, но, справедливости ради, SirEdvin «постоянно озвучивал», что в Go плохо всё и атаковал без разбору все аспекты языка – от GOPATH до интерфейсов и до отсутствия исключений. Ну, знаете, тот случай, когда предмет обсуждения не важен, а важен сам факт критики. Мои комментарии были парированием его утверждений, поэтому вам могли они выглядеть как «утверждения, что никакой проблемы нет». Другими словами, когда собеседник делит тему на белое и чёрное, то аргумент «нет, оно не чёрное» не означает, что это «белое». Это классическая fallacy of grey.

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


          1. SirEdvin
            03.09.2018 12:59
            +2

            атаковал без разбору все аспекты языка

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


            1. Обработка ошибок (и ее фиксят)
            2. Отсутствия дженериков (и как следствие, я писал, что все используют вместо них interface, но нападать на интерфейс как-то… странно, и я такого не помню).
            3. Идеи с окружением (GOPATH, версионность по коммитам вместо нормальный пакетов), и это фиксят
            4. Идеи с тем, что комментарии можно спокойной использовать как метаинформацию без отделения специальным синтаксисом. И в этом я был прав, у go теперь есть прекрасная easyjson и писать комментарии на go стало еще немного страшнее).

            Я что-то еще забыл?


        1. Edison
          03.09.2018 12:34
          +2

          SirEdvin типичный троль, его цель тут просто набрасывать. При этом человек рассуждает на счет concurrency в Go, даже не понимаю что-то такое многопоточность, говоря, что «в реальности вместо каналов в Go будет использоваться внешняя очередь типа NATS».


          1. SirEdvin
            03.09.2018 12:54

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


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


            1. Edison
              03.09.2018 13:47

              Мы разговаривали не про какие-то абстрактные воркеры, а про горутины.
              А вот ваш комментарий

              А еще обратно к реальному миру, мне кажется, вместо каналов вы чаще будете использовать какие-то другие MQ, например nats.


              1. SirEdvin
                03.09.2018 13:49

                Создается 10 goroutine, которые читают с одного канала и обрабатывают сообщение. Есть рутина, которая пишет в канал (не важно как, например, читает с файла и пишет в канал). Обработка сообщений затратный процесс (потому один пишет и много читают). Самый обычный кейс.
                При этом, после Ctrl+C завершается работа всех goroutine и после этого приложение останавливается.

                А вот ваша задача. Я воспринял это как опять же паттерн "мастер ставит задачи — воркеры выполняют". Поэтому и сказал о внешних очередях.


                1. Edison
                  03.09.2018 13:55
                  +2

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


                  1. SirEdvin
                    03.09.2018 14:00

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


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


                    1. Edison
                      03.09.2018 14:04
                      +1

                      Это тут причем?
                      Мы говорили про concurrency в Go. Вы сказали, что в питоне не хуже, потом привели пример с IPC и процессами.


                      1. SirEdvin
                        03.09.2018 14:14

                        Очевидно, потому что я немного неправильно выражаю свои мысли. Ну и еще немного неправильно в целом думал, вы меня поправили.

                        Основная идея сообщение была в том, что в python (и в других языках) тоже есть удобная возможность реализации параллельных (несколько одновременно) и конкурентных (одновременно только одно, но без ожидания) операций, которые покрывают большинство кейсов, в которых используются эти языки. И проблем с concurrency, как в страшном древнем C (которые там были, судя по слухам) не испытывают пока.

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


                        1. Edison
                          03.09.2018 14:33

                          Мы можем конечно повторить разговор — вы покажете, как сделано concurrency в питоне (с IPC и процессах), я скажу что это костыли, а вы, что «мне кажется, вместо каналов вы чаще будете использовать какие-то другие MQ, например nats.»


                          1. SirEdvin
                            03.09.2018 14:42
                            +1

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


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


                            Офф-сайд: некоторые опытные люди, например, автор fasthttp предлагают в go так же запускать по процессу на процессор (в README написано, что на ядро, но в telegram чатике go вызвали вроде как core-разработчика и он пояснил, что это старая информация и имеет смысл только в том случае, если у вас несколько процессоров, прирост почти 15-20%)


                            1. Edison
                              03.09.2018 15:35

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

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


                              Офф-сайд: некоторые опытные люди, например, автор fasthttp предлагают в go так же запускать по процессу на процессор (в README написано, что на ядро, но в telegram чатике go вызвали вроде как core-разработчика и он пояснил, что это старая информация и имеет смысл только в том случае, если у вас несколько процессоров, прирост почти 15-20%)

                              Вы сами хоть читаете ссылки, что постите?


                              Use Go 1.6 as it provides some considerable performance improvements.


                        1. 0xd34df00d
                          03.09.2018 19:09

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


      1. 0xd34df00d
        03.09.2018 19:07

        Чтобы писать experience reports, нужно обладать некоторым experience. А если знакомство с языком заканчивется на чтении примеров кода, то писать по этому опыту experience reports несколько, гм, странно. Ну, на мой взгляд. Может, там и приветствуется такое, не знаю.

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

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


        1. divan0 Автор
          03.09.2018 20:13
          -1

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


    1. divan0 Автор
      03.09.2018 11:14

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

      Ну да, поэтому в большинстве Java программ просто выбрасывается General Exception и ловится в самом верху – всегда понятно, какое исключение :)


      1. SirEdvin
        03.09.2018 11:17

        А go вместо General Exception у вас есть error и ничего не поменялось. И на java, и на go у программистов есть все возможности нормально разграничить ошибки по типам и сделать их грамотную обработку. Но почему-то все равно везде чистые error и обработка ошибок где-то на самом верху.


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


        1. creker
          03.09.2018 11:35
          -1

          Но почему-то все равно везде чистые error и обработка ошибок где-то на самом верху.

          Ваше утверждение не верно и кодом на практике не подтверждается. Обработка ошибок всегда в месте их выброса. Конкретные error, а не просто строкое значение, нужны очень редко и используются, когда это необходимо. Go никак это не заставляет делать (можно вообще ошибку игнорировать и никто не скажет ничего), эту проблему я в предложениях озвучивал на гитхабе. К счастью, команда Go изначально озвучила best practices и программисты в своем подавляющем большинстве их соблюдают. Программисты ленивые, когда им нужно менять свои привычки. Тут же привычка была привиты изначально.


          1. SirEdvin
            03.09.2018 11:38

            Ваше утверждение не верно и кодом на практике не подтверждается. Обработка ошибок всегда в месте их выброса

            У меня есть два пруфа:


            1. Почитайте в этой статье про дефолтный хендлер
            2. Вот я открыл случайный файл в moby/moby и что же я там вижу? 4 места обработки ошибок и все только то и делают, что пробрасывают ошибку наверх. Если вы так не делаете — это круто и достойно уважения, но это не так работает.


            1. creker
              03.09.2018 12:01

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


              1. SirEdvin
                03.09.2018 12:07
                +2

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


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


                1. CuredPlumbum
                  05.09.2018 16:57

                  Читал достаточно кода на Go (на Java — ещё больше), имею сказать, что в Go значительно проще понять, какие ветки породили ту или иную ошибку.

                  Тут, конечно, не только обработка ошибок, но и в целом подход к написанию программ: меньше динамики (в Java динамика — часть парадигмы языка). Но, когнитивная нагрузка при чтении исходников после введения check/handle в Go, конечно, возрастёт. Надеюсь, не сильно.


            1. divan0 Автор
              03.09.2018 12:30
              -6

              Вот я открыл случайный файл в moby/moby и что же я там вижу?

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


              1. SirEdvin
                03.09.2018 12:44
                +4

                Мне вот интересно, вы проходили какие-то курсы, как писать псевдоумные комментарии?


                Вы постоянно злоупротребляете практически всеми логическими ошибками, такими же anecdotal evidence и апелляциями к авторитету.


                Более того, абсолютно все, что вы (ну и я) пишите в обсуждениях связанных с go — является жутким субъективизмом, для которого у вас нет никакой аргументации, подкрепленной какими-либо исследованиями. То же магическое утверждение, которое мы тут обсуждаем про "модель ошибок в go заставляет программистов обрабатывать ошибки на месте", у вас есть какие-то исследования на эту тему? Правильные, с контрольной группой и прочими прелестями.


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


                1. divan0 Автор
                  03.09.2018 13:00
                  -5

                  > Мне вот интересно, вы проходили какие-то курсы, как писать псевдоумные комментарии?

                  Я правильно вас понял — вы не согласились, что ваш пример про moby это anecdotal evidence и вместо того, чтобы объяснить, где я не прав, решили сказать что мои комментарии «псевдоумные»?
                  Если какие-то «курсы» я и проходил, то исключительно по формальной логике. И ваши комментарии просто сильно выделяются обилием логических ошибок и манипуляций – от red herring (чуть выше в дискуссии с creker) до whataboutism. Кстати, о последнем – аргумент «вы тоже злоупотребляете» (вне зависимости от того, насколько он верен) это как бы оно.

                  > апелляциями к авторитету.
                  Appeal to authority это не логическая ошибка сама по себе. Если авторитет, о котором речь, действительно авторитет, то это легко может быть очень веский аргумент. Хотя в определенных случаях это да, может быть логической ошибкой.


                  1. SirEdvin
                    03.09.2018 13:06

                    Я правильно вас понял — вы не согласились, что ваш пример про moby это anecdotal evidence и вместо того, чтобы объяснить, где я не прав, решили сказать что мои комментарии «псевдоумные»?

                    А вы внимательно прочитали последний абзац моего комментария?


                    Appeal to authority это не логическая ошибка сама по себе.

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


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


                    1. divan0 Автор
                      03.09.2018 13:16
                      -6

                      практически все программирование находится вне области формальной логики

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


                      1. SirEdvin
                        03.09.2018 13:17

                        Я так понимаю, вы все еще не прочитали последний абзац моего сообщения? Я продублирую его для вас:


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

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


                        1. divan0 Автор
                          03.09.2018 13:20
                          -5

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

                          Ухты, поздравляю.


                          1. SirEdvin
                            03.09.2018 13:22
                            +1

                            Ну так вы уже прочитали и признаете, что в этом случае были не правы? Или от комментариев, в которых вы упрекаете меня в отсутствии логики будете переходить к саркастическим?


                            1. divan0 Автор
                              03.09.2018 13:24
                              -5

                              Количество derailing-а в общении с вами превышает все допустимые нормы, поэтому завершим это «общение» сарказмом, да.


                    1. 0xd34df00d
                      03.09.2018 19:11

                      Ирония состоит в том, что практически все программирование находится вне области формальной логики

                      И тут чувакам из PLT-сообщества резко стало грустно.


                      1. SirEdvin
                        05.09.2018 15:47

                        Вы про них?


                        1. 0xd34df00d
                          05.09.2018 17:56

                          Про них, или про них, или про них, и так далее.


                          1. SirEdvin
                            05.09.2018 17:58

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


                            1. 0xd34df00d
                              05.09.2018 18:06

                              Это да. Но это уже следующий вопрос.


                    1. vintage
                      04.09.2018 08:32
                      +1

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


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


                      1. divan0 Автор
                        04.09.2018 09:46
                        -1

                        В данном случае апелляция к авторитету — это демагогический приём, эксплуатирующий у людей ошибку «гугл — большая успешная корпорация,

                        И где вы такое утверждение увидели, покажете?


        1. fukkit
          03.09.2018 11:41

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


          1. SirEdvin
            03.09.2018 11:44

            Как тот несчастный, которому приходится потом поддерживать системы, от программистов, которые думают так же могу сказать — это очень плохая и просто отвратительная позиция. У меня уже собралось штук 5-7 историй, где мне хочется убивать людей за то, что они положили болт на обработку ошибок, поставили логику "если ошибка, сказать что что-то пошло не так" и мне потом пришлось в этом добре копаться и понимать "А что же пошло не так?", потому что исправлять как-то надо.


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


            1. fukkit
              03.09.2018 12:03

              Не нужно никого убивать.
              Идеальный программист в вакууме пишет идеальный код, всегда обрабатывает все ошибки, укладывается во все сроки и не существует.
              Жизнь накладывает свои ограничения.В основном, это время и деньги.
              Нет проблем делать что-то супер качественно, если: а) на это выделят время, б) за это заплатят деньги.
              Реальность в том, что в бизнесе редко кто считается с твоими (моими) личными взглядами на красоту кода.
              Бегать от реальности — глупо, менять её — не всегда возможно.


              1. SirEdvin
                03.09.2018 12:08
                -1

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


                1. fukkit
                  03.09.2018 12:28
                  +1

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

                  Причина падения важная всегда

                  Если не сталкивались с ситуациями, когда она не важна — еще столкнётесь.


                  1. SirEdvin
                    03.09.2018 12:30

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


                    1. f0rk
                      05.09.2018 15:34

                      > Или как это работает?
                      Именно так и работает


      1. anjensan
        04.09.2018 10:30
        -1

        в большинстве Java программ просто выбрасывается General Exception и ловится в самом верху

        Возможно в большинстве ваших программ так и есть.
        Но, к счастью, вы не автор большинства программ на Java.


        1. divan0 Автор
          04.09.2018 11:12

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


          1. anjensan
            04.09.2018 11:29

            Поищите, на самом деле любопытно глянуть на исследование :)

            Возможно вы имели в виду вот такой кейс:

            try {
               // Do something...
            } catch (FileNotFoundException e) {
              throw new RuntimeException("file is required blabla", e);
            }
            

            Да, так делают. В гугловой guava даже была спец функция Throwables.propogate.
            Но ее задепрекейтили и предлагают просто писать как в моем примере.

            По сути это некий аналог
            err := ... // do domething
            if err != nil {
               log.Fatalf("file is required blabla: %v", err)
            }
            


            Вы это имели в виду, когда говорили про «General Exception»?


            1. divan0 Автор
              04.09.2018 12:09

              Под General Exception я имел ввиду просто Exception.

              Вот, оригинальная ссылка neverworkintheory.org/2016/04/26/java-exception-handling.html, ссылка на сам пейпер (http://plg.uwaterloo.ca/~migod/846/current/projects/09-NakshatriHegdeThandra-report.pdf) и зеркало (оригинальная ссылка умерла вроде) www.dropbox.com/s/yhd3d818hw5dv2h/p484-kery.pdf?dl=0


              1. anjensan
                04.09.2018 13:58

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

                Глянул на секцию Аbstract. А там пишут такое:

                We found that programmers handle exceptions locally in catch blocks much of the time, rather than propagating by throwing an Exception.
                Не кажется ли вам, что это прямо противоречит вашему утверждению «просто выбрасывается General Exception и ловится в самом верху».

                Быть может вы привели не ту статью?
                Или ошиблись с переводом, когда ее читали?


                1. divan0 Автор
                  04.09.2018 16:31
                  +1

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

                  Насчёт 78% — тут даже цитата в блог посте есть:

                  Similarly, most programmers ignore Java's carefully thought out exception hierarchy and simply catch Exception (78%) or Throwable (84%) rather than any of their subclasses.

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


                  1. anjensan
                    04.09.2018 16:48

                    Ну давайте, поищите пейпер, который потверждает ваши слова.
                    А то пока тот, что вы привели, их опровергает.
                    Найдете — будет 1-1. А я пока подожду :D

                    Что несколько не то, что я сказал.
                    Давайте честно. Это прямо противоречит тому, что вы сказали. Эпитет «несколько» тут неуместен.

                    Насчёт 78% — тут даже цитата в блог посте есть:
                    Similarly, most programmers ignore Java's carefully thought out exception hierarchy and simply catch Exception (78%) or Throwable (84%) rather than any of their subclasses.
                    Раз уж привели статью, то ссылайтесь на нее, а не какой-то левоватый блог. Да и что это за проценты такие? 100% — это что? 78% от чего? 78%+84% — это как?

                    Вот вам цитата из статьи
                    Exception is a full 26% of all exceptions
                    Далее, в статье, приводятся предположения, почему так происходит. Почитайте таки статью, на которую ссылаетесь, почитайте.


                    1. divan0 Автор
                      04.09.2018 17:06
                      -1

                      Раз уж привели статью, то ссылайтесь на нее, а не какой-то левоватый блог.


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

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


                      1. anjensan
                        04.09.2018 17:15
                        +1

                        А этот второй пейпер, как раз тот, в котором результаты расходятся, как я понял из блог поста. Почитаю на досуге тоже, историю ошибок в языках программирования полезно же изучать. ;-)
                        В самом пейпере результаты не расходятся. Это в блоге числа напутали.

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

                        Умейте уже признавать свои ошибки.


                        1. divan0 Автор
                          04.09.2018 17:17

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

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


                          1. anjensan
                            04.09.2018 17:36

                            Разные статьи, может быть, тогда признаю ошибку ;)

                            Но числа то все равно совершенно левые. 78% от чего? 84% от чего? От общего количества всех try блоков? Полный бред :)

                            Вероятно это проценты от количества программистов. Т.е. мол 78% из некоего множества инженеров нет-нет да и написали разок `catch (Exception e)`. Ну, такое вполне можне быть.

                            Хотя все равно очень и очень подозрительно, что число у Throwable больше (не все начинающие джависты про него даже знают, кхекхе). Т.е. все равно выглядит как ерунда. Ну либо выборка инженеров была очень уж своеобразная (набрали где-то говнокодеров).

                            По вашему второму пейперу видно, что отлавливают `Throwable` в несколько (раз так в 6-7) реже, чем `Exception`. Откуда берутся 78% и 84% полная загадка.


                            1. divan0 Автор
                              04.09.2018 17:48

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


    1. creker
      03.09.2018 11:16
      +2

      Проблема в том, что нет информации, какая функция вернет ошибку прямо в call site. Для этого нужно лезть в прототип функции. Так что эта проблема исключений свойственна им по определению и все современные языки предлагают этому решение.


      1. SirEdvin
        03.09.2018 11:21

        В java вы обязаны декларировать ошибки, которые может вернуть функция (исключая Runtime виды ошибок). Ну и так же, обязаны их обработать или же добавить в определение функции.


        Если я правильно понимаю, что вы имеете ввиду под call site, то java дает вам информацию, какая функция какие типы ошибок может вернуть. Все остальное в руках разработчиков, которые этим управляют.


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


        1. creker
          03.09.2018 11:30

          Ну и какую информацию здесь мне дает Java?

          void Foo() throws Exception {
              foo.bar1();
              foo.bar2();
          }
          


          И это у Java описаны исключения, а у других это только в документации вроде C# и C++.


          1. SirEdvin
            03.09.2018 11:34

            Тут никакую, а если воспользоваться ide, то подсказка по функция вполне помогает.

            Ну, как я уже писал, это вопрос реализации.


            1. creker
              03.09.2018 11:36
              +1

              если воспользоваться ide

              Вот именно. Нужна ide, которая это умеет, но это не главное. Главное, что тратится драгоценное время на чтение кода. Это Go и не хочет повторять. К тому же, это не единственная проблема, которая заставила отказаться от исключений.


              1. SirEdvin
                03.09.2018 11:39
                +1

                Почитайте код написанный на go на github, хотя бы раз. После того, как вы перероете все 20 файлов в папке в поисках структуры Config вы поймете, что в go ситуация с "мне нужна ide" еще хуже.


                Это не то, что бы плохо, но вот это "не хочет повторять" не соблюдается.


                1. creker
                  03.09.2018 12:03
                  -1

                  Рыть ничего не нужно. Есть Godoc.


                  1. SirEdvin
                    03.09.2018 12:06

                    То есть вам все равно нужно что-то стороннее, что бы смотреть код. Ну и где "не хочет повторять ситуацию"? Просто страничка в браузере, вместо ide, но это не очень помогает.


                    1. creker
                      03.09.2018 12:10
                      +1

                      Сами сменили тему и теперь пытаетесь свою правоту доказать этим. Мне не нужно что-то сторонее, потому что best practices привили обрабатывать всегда ошибку. Поэтому я читаю код, и он намного более ясный, чем код с исключениями. Я вижу по call site, где и когда поток исполнения может изменить свое направление, потому что есть явный return. Это то, что хотела команда Go, отказавшись от исключений, и они своего добились. Сейчас проблема стоит совершенно другая — best practices привели к тому, что код наводнен обработкой ошибок. Этот код надо бы как-то сократить, не утратив все преимущества полученные. Это и есть задача Go 2.

                      Перечитайте с самого начала, в чем изначально проблема Java и исключений в целом. Не в документации, а в том, что call site ничего не говорит. Этой проблемы в Go нет.


                      1. vintage
                        04.09.2018 08:41
                        -1

                        Я не вижу проблемы в том, что "call site" не говорит о том, что может возникнуть ошибка в языке, где исключение может возникнуть при любом вызове. Это надуманная проблема.


                        1. creker
                          04.09.2018 12:00

                          А множество других видит, поэтому в Go, Swift, Rust пошли другим путем.

                          исключение может возникнуть при любом вызове

                          Это и есть проблема, язык таким быть не должен. Вот `Length` у массива может мне вернуть исключение? Я не знаю, может быть, это же язык, где при любом вызове может быть исключение. В документации не написано, а я ведь умный программист. Поставлю ка я лучше try/catch и в лог это плюну. Это и есть проблема, которая наводнила все exception языки. И checked exceptions в Java ничем ее не решили, как показала практика реального использования, а не фантазий, как теперь все хорошо станет.


                          1. vintage
                            04.09.2018 20:44

                            А множество других видит

                            Апелляция к толпе.


                            язык таким быть не должен

                            Почему?


                            Вот Length у массива может мне вернуть исключение?

                            Может. Более того, это может оказаться и не массив благодаря полиморфизму.


                            Поставлю ка я лучше try/catch и в лог это плюну.

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


                            Это и есть проблема, которая наводнила все exception языки.

                            Проблема-то в чём?


                      1. anjensan
                        04.09.2018 12:18

                        Ну давайте на секунду представим некий гипотетический язык, где
                        1) есть исключения
                        2) любая функия/метод, которые могут кинуть исключение, должны в конце имени иметь "!" (проверяется компилятором). Примерно как публичные методы в Go должны начинаться с заглавной.

                        И тогда ваш пример будет выглядеть так:

                        func Foo!() {
                            foo.bar1!()
                            foo.bar2!()
                        }

                        Вместо
                        func Foo() error {
                            check foo.bar1()
                            check foo.bar2()
                        }

                        Или
                        func Foo() error {
                            if err := foo.bar1(); err != nil {
                                return err
                            }
                            if err := foo.bar2(); err != nil {
                                return err
                            }
                        }


                        Все явно, call-site знает где может быть ошибка. Код не будет наводнен кучей бесползных проверок. Сразу видно, где exit-points у функции.

                        Я никого не призываю внедрять такой подход в Go.
                        Но сами по себе «исключения» не обязательно делают код «менее читабельным». Вопрос именно в реализации.


                        1. divan0 Автор
                          04.09.2018 12:33
                          +1

                          func Foo!() {
                          foo.bar1!()
                          foo.bar2!()
                          }


                          Я только со второго раза увидел там!.. Думаю в Go таких односимвольных синтаксических элементов избегают не просто так.


                          1. anjensan
                            04.09.2018 12:58

                            Ну вот опять идут придирки к конкретной реализации :)

                            Причем «субъективные» придирки "я не увидел". Не знаю, может вам не хватат подсветки синтаксиса (я бы такие методы у себя жирным подсвечивал). Может вы читали так много кода на Go, так бегло, что мозг уже игнорирует вещи, которых быть не должно. А может у вас плохой монитор. А может стоит проверить зрение. Много вариантов.

                            В любом случае, я не предлагал/ю внедрять такое в Go.

                            Да… можно было бы думать, если бы язык только разрабатывался…
                            Но сейчас уже, конечно, поздно пить боржоми :D

                            Я лишь хотел показать, что наличие исключений не обязательно означает

                            Проблема в том, что нет информации, какая функция вернет ошибку прямо в call site.


                            1. divan0 Автор
                              04.09.2018 16:39

                              А может у вас плохой монитор.

                              LG 38 inch ultra-wide, рекомендую, кстати, люто.

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

                              В черовниках обсуждается вариант использования `?` вместо `check` и он не кажется удачным. Исключения, опять же, имеют и другие проблемы помимо неявности (stack unwinding, неочевидность того, какой код будет исполнен при ошибке и т.д).


                              1. anjensan
                                04.09.2018 17:10

                                LG 38 inch ultra-wide, рекомендую, кстати, люто.
                                Ну значит проблема не в мониторе. Вычеркиваем из списка…

                                Исключения, опять же, имеют и другие проблемы помимо неявности (stack unwinding, неочевидность того, какой код будет исполнен при ошибке и т.д).
                                Имеют, не имеют… Я не собираюсь обсуждать это в текущей ветке.

                                Тут утверждалось, что с исключениями «нет информации, какая функция вернет ошибку прямо в call site». Это не обязательно не так. Зависит от того, как сделать язык.

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

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


                                1. divan0 Автор
                                  04.09.2018 17:22

                                  То что вы привели в пример – это не исключения. Фраза «от реализации зависит» не делает это исключеним тоже. Так что сорри, пример мимо.


                                  1. anjensan
                                    04.09.2018 17:43

                                    А почему это не исключения?

                                    Или если мы вводим в язык строгий гайдлан «любая функция, которая может кинуть исключение, должна в имени содержать такойто суффикс», то это уже не исключения?


                                    1. divan0 Автор
                                      04.09.2018 17:51

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


                                      1. anjensan
                                        04.09.2018 17:54

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

                                        Повторю специально для ВАС
                                        Тут утверждалось, что с исключениями «нет информации, какая функция вернет ошибку прямо в call site». Это не обязательно так. Зависит от того, как сделать язык.


                                        1. divan0 Автор
                                          04.09.2018 17:55

                                          и этим аргументируете о читабельности последних.

                                          Не придумывайте за меня того, чего я не говорил :)

                                          Ваша же цитата:

                                          Но сами по себе «исключения» не обязательно делают код «менее читабельным».


                                          1. anjensan
                                            04.09.2018 17:59

                                            Хах. Подловили :)

                                            Правда я говорил «не обязательно делают менее читабельным», а не «делают код более читабельным» (как приписываете вы). Но я все же поправлюсь:

                                            Но сами по себе «исключения» не обязательно означают, что нельзя напрямую из кода узнать, какая функция вернет ошибку прямо в call site. Вопрос именно в реализации.

                                            Такая формулировка лучше? :)


                                            1. divan0 Автор
                                              04.09.2018 18:09

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


                                    1. anjensan
                                      04.09.2018 17:52

                                      А, я кажется понял.

                                      Вы не прочитали мой изначальный комментарий, только увидели код. Ну и подумали что это я предлагаю заменить `check` на `!`. Нет, не предлагаю. Прочитайте мой комментарий :)


                                      1. divan0 Автор
                                        04.09.2018 17:53
                                        -1

                                        Нет, я его и прочёл. Просто давно потерял нить того, с кем/чем (и зачем) вы спорите и что пытаетесь доказать :)


              1. Ryppka
                04.09.2018 14:40

                Главное, что тратится драгоценное время...


                Хм, а на… ч на хабре оно не, не тратится? ;)


  1. Sly_tom_cat
    03.09.2018 12:07
    +1

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


  1. SONANT
    03.09.2018 12:21
    +1

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


  1. Alesh
    03.09.2018 17:39
    +3

    Несмотря на годы скачек по граблям, религия так и не позволила им взять и просто впилить try/exception/finally. Такая верность убеждениям заслуживает похвалы. хихихи.


    1. divan0 Автор
      03.09.2018 20:14

      Что за религия?


      1. Alesh
        03.09.2018 23:25
        -2

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

        func CopyFile(src, dst string) throws error {
            try {
                try {
                    r := os.Open(src)
                    w := os.Create(dst)
                    io.Copy(w, r)
                } finally {
                    r.Close()
                }
            } finally {
                w.Close()
            }
        }
        

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


        1. Edison
          03.09.2018 23:43
          +1

          о там что бы сделать удобно

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


        1. divan0 Автор
          04.09.2018 00:00

          Полагаю, вы не читали статью – в ней объясняется, в чём проблемы исключений и почему их нет в Go.

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

          Я правильно вас понял, что вы считаете, будто исключений в Go нет не потому, что авторы Go в них видят проблемы и неудобства, а потому что просто хотели сделать «не как у других»?


          1. Alesh
            04.09.2018 08:55

            Из вашего текста абсолютно не понятно в чем же принципиальная отличие check/handle от try/catch. Да есть фраза «существенно отличается от исключений», а вот чем, я не понял. Если сможете сформулировать коротенько и понятно здесь, то было бы прекрасно.


            1. divan0 Автор
              04.09.2018 10:08

              Если кому-то не понятна разница между check/handle и try/catch, то правильнее спросить в комментарии «в чём разница», а не наезжать на авторов языка. Попробуйте в следующий раз.

              Разница же достаточно существенна:
              — в catch/handle проверка ошибки очень явная – check пишется прямо перед вызовом функции (а не на целый блок, в котором могут быть функции, выбрасывающие ошибки). в этом плане оно похоже на try! из Rust или Swift.
              — нет неявной развертки стека. максимум, что handler может сделать – пробросить наверх выше. тоесть, по сути, это синтаксический сахар для того, что вы могли бы делать в `if err != nil` блоке
              — всегда легко понять, какой именно код будет исполнятся при check – с исключениями это не так: множественные catch конструкции будут вызываться в зависимости от ошибки.


              1. Alesh
                04.09.2018 10:30

                Учитывая что в блоке try может быть вызвана всего лишь одна функция, а в с++ и др. языках даже не обязательно явно выделять блок фигурными скобками, то вы получаете ровно то что и catch/handle, за исключением конечно, что у вас сначала определяется обработка ошибки (catch), о потом проверка не произошла ли она (try). Извините, но я не вижу отличий, за исключением того что все поставлено с ног на голову, но это в духе религии Go.


                1. divan0 Автор
                  04.09.2018 11:15

                  Учитывая что в блоке try может быть вызвана всего лишь одна функция

                  Вы что-то путаете. В классическом понимании исключений try..catch в блоке try можно писать сколько угодно инструкций.


                  1. Alesh
                    04.09.2018 13:05
                    -1

                    В классическом понимании исключений try..catch в блоке try можно писать сколько угодно инструкций.

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


                    1. divan0 Автор
                      04.09.2018 16:40

                      Вы, наверное, и с компилятором так общаетесь :) Удачи.


                1. creker
                  04.09.2018 12:09

                  Это у вас все с ног на голову. Ни исключений не понимаете, ни статьи предоставленной. Фундаментальное отличие в том, как обработка ошибок влияет на control flow. Исключения полностью его меняют, перелопачивая весь стэк, пока не найдется catch блок. Здесь же всего лишь синтаксический сахар для `if {return }` с бонусом в виде `handle` блоков. Control flow остается очевидным и читаемым по тексту программы. Если и так непонятно, но вперед читать ошибки в Swift и Rust. Там тоже нет никаких исключений и механизм примерно тот же, какой хотят в Go.


                  1. eao197
                    04.09.2018 12:44
                    -1

                    Если и так непонятно, но вперед читать ошибки в Swift и Rust. Там тоже нет никаких исключений и механизм примерно тот же, какой хотят в Go.
                    Сможете показать обработчик ошибки в Rust, который определяется задолго до места, в котором ошибка диагностирована?


                    1. Edison
                      04.09.2018 13:01

                      ? и unwrap


                      1. eao197
                        04.09.2018 13:04

                        Спасибо, повеселили.


                        1. Edison
                          04.09.2018 13:07

                          а что не так? Если не определять handler то будет использоваться дефолтный handle err { return nil } — как в Rust c?..
                          В тоже время можно определить свой в начале метода/функции или обработать ошибку в месте, где возникла if err != nil {}.


                          1. eao197
                            04.09.2018 13:12

                            а что не так?
                            Все.

                            '?' в Rust — это частично то, что в Go предлагают под соусом ключевого слова check. Но лишь частично. Ранее '?' был представлен в виде макроса try! Задача '?' и 'try!' лишь в том, чтобы проверить результат операции и, если результат отрицательный, возвратить его в вызывающую функцию. Все. Никакой обработки ошибок ни '?', ни 'try!' не задействует.

                            Обработка реализуется в вызывающей функции посредством обычного паттерн-матчинга (либо через match, либо через if let). И, что важно, обработчик ошибки в Rust-е пишется там же, где результат операции проверяется.

                            Там же.

                            А не N строчками выше. Да еще неявно провязанными в цепочки.

                            unwrap — это вообще из другой области. Вот вообще.


                            1. Edison
                              04.09.2018 13:16

                              У вас вот тут противоречие:

                              Задача '?' и 'try!' лишь в том, чтобы проверить результат операции и, если результат отрицательный, возвратить его в вызывающую функцию.

                              И
                              Все. Никакой обработки ошибок ни '?', ни 'try!' не задействует.

                              Тоже самое будет делать check c дефолтным хендлером.

                              Обработка реализуется в вызывающей функции посредством обычного паттерн-матчинга (либо через match, либо через if let). И, что важно, обработчик ошибки в Rust-е пишется там же, где результат операции проверяется.

                              Как и в Go c `if err != nil`


                              1. eao197
                                04.09.2018 13:21

                                Нет никакого противоречия. Просто вы почему-то:

                                * считаете, что вызов дефолтного хендлера — это не вызов хендлера ошибок, а так, ничего;

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

                                Хорошая попытка натянуть сову на глобус, но нет.


                                1. Edison
                                  04.09.2018 13:28

                                  нет, я считаю, что
                                  * вызов дефолтного хендлера это обработка ошибки, так же как `?` в Rust — это обработка ошибки. Так же как `unwrap` это обработка ошибки — бросает панику если есть ошибка.

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

                                  Я вам привел пример того, что в Rust есть «обработчик ошибки в Rust, который определяется задолго до места, в котором ошибка диагностирована»


                                  1. eao197
                                    04.09.2018 13:30
                                    -2

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


                                    1. Edison
                                      04.09.2018 13:32
                                      +1

                                      Диагностирование есть часть с проверкой, обработка — это возвращение ошибки.

                                      if err != nil { // диагностирование
                                          return err // обработка
                                      }
                                      


                                      1. eao197
                                        04.09.2018 14:02
                                        -1

                                        Феерично.


                                        1. Edison
                                          04.09.2018 16:35
                                          +1

                                          отличный комментарий.
                                          Вы считаете, что все что в теле `if err != nil` все еще диагностирование?


                                          1. 0xd34df00d
                                            05.09.2018 18:06

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


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


              1. anjensan
                04.09.2018 10:43

                check пишется прямо перед вызовом функции (а не на целый блок, в котором могут быть функции, выбрасывающие ошибки). в этом плане оно похоже на try! из Rust или Swift.
                Любопытно, можно ли будет делать так:
                handle err { ... }
                // ...
                check func() error {
                     // ...
                }()
                

                И если можно (не вижу причин в обратном, можно ведь передавать анонимную функцию в блок defer), как по вашему личному мнению, будут ли программисты активно пользоваться такой возможностью?

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


                1. divan0 Автор
                  04.09.2018 11:17

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

                  Полагаю что, не чаще, чем сейчас (объявление анонимной функции и вызов тут же).


        1. creker
          04.09.2018 00:18

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


        1. Sirikid
          04.09.2018 17:19
          +1

          У вас скоп сломался, если брать вариант как в Java/C# то до должно быть что-то вроде


          func CopyFile(src, dst string) throws error {
              try (r := os.Open(src); w := os.Create(dst)) {
                  os.Copy(w, r)
              }
          }


          1. Alesh
            04.09.2018 17:26
            +1

            мда, впрочем воздержусь от ответа, а то меня точно забанят. бгг


        1. nikbond
          04.09.2018 21:11

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

          func (File f) Destructor() {
                      f.Close()
          } 
          
          func CopyFile(src, dst string) throws error {
                      r := os.Open(src)
                      w := os.Create(dst)
                      io.Copy(w, r)
          }


          Простите, С++ в голову ударил.


          1. divan0 Автор
            04.09.2018 21:12

            А кто удалит недозаписанный файл в случае ошибки io.Copy(w, r)? :)


            1. 0xd34df00d
              05.09.2018 18:20

              А можно было бы вообще не париться и


              copyFile src dst = do
                r <- openFile src
                w <- openFile dst
                copyResult <- copy r w
                whenLeft copyResult $ \_ -> removeFile w

              GC файлы закроет при следующем minor gc. Охота закрыть сразу?


              copyFile src dst =
                bracket (openFile src) closeFile $ \r ->
                bracket (openFile dst) closeFile $ \w ->
                do
                  copyResult <- copy r w
                  whenLeft copyResult $ \_ -> removeFile w


  1. Alesh
    03.09.2018 20:44
    +1

    гугли «религия не позволяет», только обязательно в скобочках


  1. MacIn
    04.09.2018 16:58

    Это приятный, чистый и элегантный код. Он также некорректый: если io.Copy или w.Close завершатся неудачей, данный код не удалит созданный и недозаписанный файл.

    Это решается конструкцией finally…


    1. divan0 Автор
      04.09.2018 21:18

      Очевидно же, что не решается. Тут нужно удалить файл, только если две из операций (Copy и Close) вернули ошибку, в остальных случаях – не нужно.