Go: предложение: оставить в покое "if err! = nil"?

Созданный на 28 июн. 2019  ·  314Комментарии  ·  Источник: golang/go

Предложение Go2 № 32437 добавляет к языку новый синтаксис, чтобы сделать шаблон if err != nil { return ... } менее громоздким.

Существуют различные альтернативные предложения: # 32804 и # 32811, поскольку оригинальный вариант не всем нравится.

Добавим еще одну альтернативу: почему бы не оставить все как есть ?

Мне понравился явный характер конструкции if err != nil и поэтому я не понимаю, зачем нам для этого нужен новый синтаксис. Это действительно настолько плохо?

FrozenDueToAge Proposal Proposal-Hold error-handling

Самый полезный комментарий

должен быть только один способ сделать что-то

Все 314 Комментарий

Я поддерживаю это. Мне очень нравится, как оформление каждой ошибки перед ее возвратом добавляет к источнику удобочитаемую документацию (обычно мы форматируем наши ошибки как «не удалось [что я делаю в этих строках кода]: [предыдущая ошибка]»), а также для пользователей ошибки чтения.

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

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

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

Мне также нравится явная проверка ошибок. try сбивает с толку, а неявный возврат - странный.

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

Вот пример, с которым я не обязательно согласен:

value, err := foo()
return err if err != nil

Это позволило бы использовать более короткий, но все же явный подход. И это позволило бы добавить контекст!

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


РЕДАКТИРОВАТЬ: я добавил предложение для этого здесь: https://github.com/golang/go/issues/32860

должен быть только один способ сделать что-то

[...] Почему бы не оставить все как есть?

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

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

Так много этого.

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

Я не уверен, действительно ли новая конструкция решит эту проблему. Если вы обернули if err := nil во вспомогательную функцию вроде этой, это может немного помочь (простите за мой ржавый Go):

func handleErr(err error, cb func(error)) {
        if err := nil {
                cb(err)
        }
}

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

Я поддерживаю это. if err != nil { return err } не является частью кода в нашей базе кода. Следовательно, попытка "макроса" вообще не имеет смысла. Мы возвращаем только обернутые ошибки с сообщением, описывающим контекст.

Добавление контекста через defer также не имеет смысла, поскольку мы хотим возвращать разные сообщения об ошибках, чтобы различать разные типы ошибок. Хотя try(fn(), "my error message: %w") может оказаться полезным. Но даже в этом случае конструкция if err != nil может быть предпочтительнее из-за меньшей длины строки.

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

type Result<T> interface {
  Expect(err error) T
  OrElse(defaultValue T) T
}

func From<T>(value T, err error) Result<T> { ... }

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

Текущее предложение try , не имеющее возможности явно обозначить ошибки, не соответствует моим потребностям. Я не могу себе представить, чтобы когда-нибудь использовал это. Откровенно говоря, с таким же успехом его можно было бы назвать code_smell .

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

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

if err != nil {
  return err
}

Это обработка ошибки nil . Ни в одной точке этого шаблона не обрабатывается значение ошибки.

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

begin
 some_method_that_raises_an_error
rescue => e # catch any exception
  retry e        # throw it up again
end

Это передает то же поведение, что и код golang. Когда мы обнаруживаем, что произошло исключение, а затем повторно вызываем его. Мы просто бросаем его в стек.

В голанге мы return it.

Где собственно происходит _обработка_ ошибок?

У всех нас был похожий опыт несостоятельности этого паттерна. Например, получение ошибки file not found и последующее отслеживание исходного _thrower_ этой ошибки.

Вот почему я считаю предложение try (и другие) ошибочным. У нас нет хорошего шаблона для фактической обработки ошибок.

Я видел проверку строки err.Error() , утверждения типа и т. Д., Чтобы действительно проверить ошибку.
Нам нужен шаблон для этого несоответствия. Кажется, что xerrs может решить эту проблему, но это еще не все.

Я поддерживаю сохранение проверки err! = Nil как есть.

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

  • Эти кодовые пути так или иначе существуют.
  • Даже если вам не требуется скрывать кодовый путь, предоставление людям возможности скрыть его сделает это поведение по умолчанию (потому что, очевидно, мы все еще измеряем, насколько сложно использовать язык, по количеству строк).
  • Если поведение по умолчанию скрывает пути кода, тогда я буду искать новые ошибки «отсутствующей очистки».
  • Значение и шаблоны для возвращаемых ошибок достаточно разнообразны, чтобы это предложение отражало только часть предполагаемой проблемы.
  • Если будет захвачена только часть, мы обязательно получим кучу решений
  • С кучей решений возникнет соблазн использовать адаптивную магию для сценариев использования, чтобы свести их
  • Что, если бы это действительно было проблемой, люди могли бы создать собственное простое решение или использовать какой-нибудь массовый паттерн. Я ничего подобного не видел. Может, я просто недостаточно внимательно присмотрелся.

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

Если можно, я считаю, что это ответ. Это новое предложение об ошибке прямо противоречит целям языка.

Причина, по которой я люблю golang, заключается в его простоте и понятном использовании потока управления. Одна из вещей, которые я больше всего ненавижу в Java, - это конструкция try throw. Это так противно. Это поощряет ужасную обработку ошибок. Отправка исключений в стек вызовов - ужасный и отвратительный метод обработки потока управления. Кроме того, он поощряет завернуть все в гигантский чек и прекратить его выполнение вместо самодокументирования и явной обработки каждой ситуации с ошибкой.

Если err! = Nil способствует хорошей обработке ошибок, самодокументируется и поощряет хорошую документацию по конкретному случаю, и, честно говоря, это одна из вещей, которые мне больше всего нравятся в go. Прерывание этого нового потока управления с использованием беспорядочных, несколько неоднозначных результатов и параметров, а также запутанной семантики - не в духе языка, который я полюбил.

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

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

Это нехорошо, если вопросы разделены, но я думаю, что в этом случае два мнения объединяются в одно.

  1. Нам не нравится новый синтаксис (попробуйте или новый синтаксис if-err)
  2. В любом случае, мы не хотим добавлять новый синтаксис

Значки голосования GitHub не могут интерпретировать второй.

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

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

Я лично согласен с тем, чтобы оставить обработку ошибок как есть. Что мне нравится в Go, так это то, что язык минимален и, вообще говоря, имеет один способ делать что-то. Добавив новый синтаксис для обработки ошибок, мы создадим мир, в котором x% кода использует текущий метод, а y% использует новый метод. Это, среди прочего, уже обсуждалось, приведет к созданию несовместимых кодовых баз. Я лично не думаю, что ценность нового синтаксиса обработки ошибок стоит компромиссов, поскольку я считаю существующий синтаксис достаточным / достаточным.

Как новичок в Golang, одна из вещей, которые меня освежают в языке, - это явная обработка ошибок. Я довольно много работал с Java, Ruby, Python и Node, и работа с ошибками намного сложнее, чем в Go. Я бы предпочел видеть четкий «путь» ошибок, чем предполагал, что он подразумевается какой-то языковой конструкцией, делающей его более расплывчатым.

«Верни ... если ...» предложение от @andreynering на самом деле довольно умное, imho. Сохраняет код явным (без скрытого прерывания потока управления), сокращая шаблон (однострочный).

Согласитесь, оставьте if err != nil покое.

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

Я написал # 32804 и предпочел бы, чтобы что-то НЕ изменилось. Если ваш код длинный, это потому, что он много чего делает. Если у вас много кода обработки ошибок, это потому, что вы хорошо справляетесь со всеми своими случаями.

Пожалуйста, давайте не будем добавлять вещи только ради добавления вещей.

Мне нравится простота обработки ошибок как есть.

Expect - это просто анаграмма для except, и я бы предпочел не использовать ее. Спасибо, что начали.

Пожалуйста, не меняйте мой Святой Грааль.

Огромное количество откликов сообщества требовало более рациональной обработки ошибок (из ежегодного опроса). Команда Go сейчас занимается этой проблемой.

@icholy Конечно, но текущие предложения оставляют желать лучшего. Кажется, что все они либо запутывают обработку ошибок, либо возвращаются к большему количеству реализаций стиля try / catch / finally, выводят обработку ошибок из контекста или иным образом усложняют ее. Поскольку Go должен быть простым языком, я думаю, многие из нас надеялись на простой вариант. Я не видел ничего, что мне нравилось бы, поэтому я думаю, что лучший вариант - сохранить текущий шаблон.

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

@kevineaton вы думаете, что try - это сложно?

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

@icholy, потратив десять лет на написание Java и Python до Go, думаю, может. Я думаю, вы сталкиваетесь с перехватом исключений Pokemon или перехватом нескольких исключений, и в противном случае вводите еще больше накладных расходов и шаблонов. Я бы не стал возвращаться к этому стилю обработки ошибок, если бы мог когда-либо его избежать, поскольку он почти всегда приводил к головным болям и путанице, ОСОБЕННО при обучении. Я также преподаю информатику в дополнение к моей повседневной работе в качестве архитектора программного обеспечения, поэтому я склонен к обучению новых разработчиков и наставничеству. Я бы выбрал Go, и это простая обработка ошибок, а не более потенциально более сложная или тонкая обработка ошибок в любой день.

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

Разве это не правда. Но вот и мы.

if err != nil не исчезнет, ​​если будет добавлен try . Я считаю, что try добавит некоторую ясность к путям кода, которые либо связаны с пересылкой ошибок, либо где множество различных ошибок можно легко суммировать в одном обработчике ошибок defer. . Я действительно не понимаю, как try побуждает не обрабатывать ошибки намного больше, чем кучу пустых if-err-return-err . Фактическую обработку ошибок легко игнорировать, независимо от того, есть ли try . Я думаю, что try - одно из лучших предложений по обработке ошибок, потому что похоже, что код, который его использует, будет легко читать.

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

Из FAQ по Go

Почему в Go нет оператора?:?
_В Go нет операции тройного тестирования. Для достижения того же результата вы можете использовать следующее: _

if expr {
   n = trueVal
} else {
    n = falseVal
}

Причина отсутствия?: В Go заключается в том, что разработчики языка видели, что операция используется слишком часто для создания непостижимо сложных выражений. Форма if-else, хотя и длиннее, но, несомненно, яснее. Для языка требуется только одна условная конструкция потока управления .

@ianlancetaylor

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

Вы можете ответить на конкретное сообщение. Я только что ответил на твое. :)

Поскольку здесь нет настоящего предложения, это просто ответ на другие предложения,

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

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

@kevineaton ты закончил сосать свой собственный член?

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

Не менять язык для удаления if err != nil - это прекрасное предложение, которое практически не требует дополнительных деталей. Я не уверен, в чем проблема. Нет, это не бог ужасно долго и тяжело грокать. Это не значит, что это неправильно, плохо или недостаточно.

+1, если нет ничего лучше, хорошо будет действительно хорошая информация о трассировке стека (без танцевальных кадров), я думаю, x/errors уже достигли этого, но я бы хотел что-то аля быстрое в ближайшем будущем, например, маркировку func s с использованием ключевого слова throws , которое вернет ключевое слово error + try , предотвращая затенение ошибок var (что я лично ненавижу), что-то вроде этого:

func a() (int) throws {
  throw &someError{}
}

anInt, err := try a()

@icholy Это было невероятно ненужным. Это место для обсуждения, и сообщество Go должно быть приветливым. Здесь нет места для такого рода замечаний. Я считаю, что Сократу было что сказать об оскорблениях в дебатах.

Текущая обработка ошибок подвержена человеческим ошибкам. Достаточно легко забыть проверить err в данный момент. Если в области уже есть какие-либо проверки (а в большинстве случаев они есть), компилятор не завершит работу с unused variable . Обработка ошибок должна быть строгой - вы либо _ ошибку, или проверяете ее - стрельба по ноге не должна быть возможна.

@kevineaton вы думаете, что try - это сложно?

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

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

Блоки try сводят на нет всю чертову цель многократного возврата.

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

Текущая обработка ошибок подвержена человеческим ошибкам. Достаточно легко забыть проверить err в данный момент. Если в области уже есть какие-либо проверки (а в большинстве случаев они есть), компилятор не завершит работу с неиспользуемой переменной. Обработка ошибок должна быть строгой - вы либо _ ошибку, либо проверяете ее - стрельба по ноге не должна быть возможна.

@fillest Предлагаемое изменение обработки ошибок упрощает «

Я перестал использовать Go из-за отсутствия универсальных шаблонов, склонности к шаблонам, GC, отсутствия ограничений ресурсов / учета и рабочей нагрузки, создаваемой PHP-новичками, которые не понимали, что делает компилятор. Haskell, C # и другие неплохо решили обработку ошибок ... предложение Go 2 выглядит нормально, если в нем, как и раньше, есть явная обработка регистра (не уверен).

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

Полностью согласен, поскольку встроенный try не поможет обернуть ошибки и добавить к ним информацию даже ненадолго.

Перед переписыванием с помощью try :

_, err := doSomething()
if err != nil {
    return nil, errors.Wrap(err, "failed to do something")
}

_, err = doOtherThing()
if err != nil {
  return nil, errors.Wrap("failed to do the other thing")
}

Представьте, что будет после перезаписи с try .

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

try(extract_value(try(get_data(1), errors.Wrap(err, "failed to get data")), errors.Wrap(err, "failed to get data")))

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

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

if err != nil {
    return _, _, err
}

@sorenvonsarvort мне это не кажется таким уж плохим:

var errContext string 

defer func() {
  // err is a named return
  if err != nil {
    err = fmt.Errorf("%v: %w", errContext, err)
  }
}()

errContext = "failed to do something"
_ := try(doSomething())

errContext = "failed to do other thing"
_ := try(doOtherThing())

Насколько я понимаю, вы также можете использовать if err != nil { ... } если это более понятно для этого конкретного раздела кода.

try светится в остальных случаях. Представьте себе что-то вроде:

func trySomeComplexOp() (r result, err error) {
  a := try(step1())
  b := try(step2(a))
  c, d := try(step3(b))
  return try(lastStep(c, d)), nil
}

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

Огромное количество откликов сообщества требовало более рациональной обработки ошибок (из ежегодного опроса). Команда Go сейчас занимается этой проблемой.

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

@sirkon, на чем вы основываете это заявление?

@sorenvonsarvort мне это не кажется таким уж плохим:

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

В России мы называем это «экономия на спичках». Используйте Google Translate, чтобы понять смысл.

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

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

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

f := try(os.Open(filename))

становится

f := try(os.Open(filename), "open data file")

Конечно, если вам нужно сделать намного больше, по-прежнему доступен «полный» способ проверки err != nil .

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

Но мой предварительный опыт с изменением, кажется, подтверждает, что это действительно не нужно. У меня есть 2 программы «реального мира» с примерно 10 тысячами строк каждая, и я запустил показав, что ни одна из них не выиграет от этого изменения. Это легко объясняется тем фактом, что оба всегда добавляют контекст к ошибкам. У меня есть другие небольшие "игрушечные" программы в Go, и tryhard нашел 1 случай, когда я мог бы использовать try в одной из них, но это все.

Я допускаю, что другие люди могут относиться к ошибкам иначе, чем я, и допускаю возможность положительного использования try . Сам исходный код tryhard имеет несколько последовательных случаев return err , что, если бы он использовал try я не думаю, что разборчивость будет настолько нарушена. Но я просто опасаюсь неправильного использования, потому что это повлияет на разборчивость. Хорошим примером может служить здесь . А потом определение того, что использовать, а что нет, - это совсем другая история.

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

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

Огромное количество откликов сообщества требовало более рациональной обработки ошибок (из ежегодного опроса). Команда Go сейчас занимается этой проблемой.

@icholy Как я уже сказал, мне нравится добавлять контекст к ошибкам. В этом отношении «более оптимизированная обработка ошибок» для меня означает более эффективные способы предоставления этого контекста и получения из него информации. Например, со всем контекстом, который я добавил к своим ошибкам, должно быть тривиально спросить «контекст», была ли ошибка вызвана тайм-аутом. Но это не так. Обычно вам приходится прибегать к использованию pkg/error , создавать свои собственные «структуры ошибок» и / или создавать методы для работы с ними, которые в зависимости от реализации могут прибегать к поиску строк. Я бы предпочел увидеть что-то, что избавит меня от создания целых структур и методов, чем то, что сэкономит мне один if в лучшем случае. И, как было сказано ранее, можно ли назвать это «обработкой ошибок», когда это изменение в основном обеспечивает более удобный способ не обрабатывать ошибку?

вы думаете, что try - это сложно?

Изолированно try не является сложным, но изменения языка не рассматриваются изолированно. Рассмотреть возможность:

  • Повышенная когнитивная нагрузка от изучения семантики новой встроенной функции
  • Снижение удобочитаемости из-за использования try потому что оно короче, в случаях, когда вместо него следовало использовать if err != nil {return ... errors.Wrap() }

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

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

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

Что, если бы gofmt был изменен таким образом, чтобы блоки оставались в одной строке с одним оператором?

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

Вот как бы это выглядело:

// we already have an err in scope
err = frub.Confozzle(foo, bar, baz)
if err != nil { return errors.Wrap(err, "confozzling didn't work") }

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

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

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

Если бы я мог просто уменьшить

if err := foo.x(a, b); err != nil {
    return err
}

if err := foo.y(); err != nil {
    return err
}

if err := foo.z(c); err != nil {
    return err
}

к чему-то вроде

if err := check foo.x(a, b), foo.y(), foo.z(c); err != nil {
    return err
}

может быть , тоже было бы здорово, не меняя слишком много идиомы ИМХО.

@henvic Я думаю, проблема в том, что вы предполагаете, что хотите обрабатывать три ошибки таким же образом. Go заставляет задуматься о том, как обрабатывать каждую ошибку индивидуально. Это ясно показывает наличие отдельных проверок ошибок. Объединение их в одну заставляет разработчика мысленно вернуться и проверить, действительно ли эти ошибки нужно обрабатывать таким же образом? Я думаю, что с этим предложением мы теряем ясность и заставляем задуматься над ошибками, сделав несколько меньше ключевых моментов.

@sanbornm , ты прав. Я согласен.

Этот check тоже может быть оператором checkNonZero , который принимает только один возвращаемый аргумент и возвращает первое ненулевое значение (например, null). Но помимо того, что он слишком расплывчатый и еще больше влияет на язык. Это даже лишь слегка намекнет, что вы не должны использовать его, если ожидаете, скажем, io.EOF. Другой возможностью было бы, возможно, рассчитывать на go vet чтобы по крайней мере сообщить вам о наиболее распространенных случаях (например, io.EOF при чтении из канала) ... но эта идея звучит не очень хорошо для все мне.

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

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

В больших проектах худшие журналы создаются функцией, которую мы забыли, кто-то написал, которая вызывает библиотеку, которая вызывает библиотеку, которая вызывает библиотеку, которая генерирует общий new Exception() который ничего не улавливает, пока обработчик исключений "Pokemon" не сделает и зарегистрировал общую ошибку. Второе худшее - то же самое, но с непостижимой трассировкой стека в несколько сотен строк, с помощью которой, я думаю, мы сможем в конечном итоге выяснить причину (к счастью, просто поиск github.com/<us>/<ourproject> находит большую часть соответствующей информации, но иногда бывает много). Несмотря на свое название, «Исключения» крайне редко встречаются в больших Java-проектах.

Между тем, даже когда существует много избыточного контекста, простые строки ошибок Go, такие как "narf: Error unpoiting the zort: foo: Unexpected bar in baz: {\"ork\": \"morpork\"}" , обычно (по моему опыту) очень легко интерпретировать, если мы стараемся встраивать важный контекст где-нибудь в фактическое значение ошибки. Если оказывается, что важный контекст отсутствует, это тоже обычно довольно очевидно. «Исправление» в этих случаях - это добавление большего контекста и ожидание другой ошибки, так что это не идеально, но в целом я по-прежнему предпочитаю это прокрутке трассировок стека и / или полагаться на зависимости зависимостей моих зависимостей, чтобы «выбросить "или" поднять "нормальные сообщения об ошибках. Я действительно ценю то, что имя panic() кажется, мешает большинству разработчиков Go так широко развертывать то, что по сути является одной и той же языковой функцией. В конце концов, давайте не будем делать error и panic одним и тем же.

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

func foo(a, b, c SomeArgType) (x, y, z SomeReturnType, err error) {
  handleError := func(handleErr error) (x, y, z SomeReturnType, err error) {
    log.WithFields(logrus.Fields{
      "package": "foo",
      "func": "foo",
      "arguments": map[string]SomeArgType{"a": a, "b": b, "c": c},
      "error": handleErr,
    }).Error("Error fooing the bar")
    return reasonable, default, values, handleErr
  }

  err := doABunchOfThings()
  if err != nil {
    return handleError(err)
  }
}

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

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

@thomasf

Перенос ошибок и стек фреймов / печать ошибок сделали бы намного более простым

Я согласен.

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

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

@icholy

Огромное количество откликов сообщества требовало более рациональной обработки ошибок (из ежегодного опроса). Команда Go сейчас занимается этой проблемой.

Просто чтобы ответить на это; в Великобритании также было большинство в пользу Brexit. Конечно, ЕС также несет в себе некоторые недостатки, на которые отреагировала широкая общественность. Однако, когда были рассмотрены все альтернативы, казалось, что оставаться в ЕС не так уж и плохо.

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

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

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

@icholy

Огромное количество откликов сообщества требовало более рациональной обработки ошибок (из ежегодного опроса). Команда Go сейчас занимается этой проблемой.

Просто чтобы ответить на это; в Великобритании также было большинство в пользу Brexit. Конечно, ЕС также несет в себе некоторые недостатки, на которые отреагировала широкая общественность. Однако, когда были рассмотрены все альтернативы, казалось, что оставаться в ЕС не так уж и плохо.

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

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

Нам нужны лучшие, менее противоречивые варианты, чем предложение try .
Я не вижу необходимости в поспешных решениях.

@velovix Я думаю, что ненавижу параметрический полиморфизм больше, чем "обработку" ошибок try / catch, но если бы он действительно стал функцией языка, я мог бы увидеть несколько способов обойти необходимость в другой встроенной функции языка.

Во-первых, когда код, который люди не хотят повторять, выглядит как шаблон:

foo, err := Foo()
if err != nil {
  log(err)
}
bar, err := Bar(foo)
if err != nil {
  log(err)
}
// ...

Тогда некоторая комбинация параметрических функций, вывода типов и шаблонов проектирования _maybe_ или _optional_ стилей напрямую (хех) уменьшит шаблон, не прибегая к странным стратегиям нелинейного потока управления:

func<T> DoWithErrorLogging(f func(any...) (T, error), args... any) T {
  t, err := f(args...)
  if err != nil {
    log(err)
  }
  return t
}
// ...
foo := DoWithErrorLogging(Foo)
bar := DoWithErrorLogging(Bar, foo)

ИМО это все было бы намного хуже, чем Go1. Но лучше, чем иметь это _plus_ try / catch ключевые слова на языке.

Честно говоря ... В настоящее время я думаю, что мои любимые "критические" изменения для Go2 просто исправят все небольшие неудобства в Go1, например, net/http умолчанию являются изменяемыми общими глобальными объектами, вложенными в изменяемые общие глобальные объекты (просто сделать Hashicorp cleanhttp стандартным, в основном), или (*time.Timer).Reset() с бесполезным возвращаемым значением, о котором вам просто нужно знать, или весь пакет системных вызовов . Go3 может быть выпущен почти сразу после этого с любыми опухолями, которые люди хотят вырастить на нем; Я не понимаю, почему маленькие и большие критические изменения нужно делать в одном выпуске.

Я за try ... при умеренном использовании. Я подозреваю, что популярные проекты добавят рекомендации о том, когда / если они согласны с использованием try, и что небольшие / новые / индивидуальные проекты когда-нибудь будут страдать от плохих ошибок - и, следовательно, от отсутствия использования - из-за try ing слишком часто. Эти проекты умрут, будут исправлены или разветвлены.

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

Если добавить try , я, вероятно, буду использовать его в некоторых случаях. Это такие случаи, когда возвращается ошибка, но я думаю, что это настолько невероятно маловероятно, что действительно будет ошибка, что я не вижу смысла в добавлении контекста, и я просто возвращаю ошибку как есть. Например, если я только что создал файловую систему, которая заполняет диск размером 1 ТБ, я могу быть уверен, что есть место для создания файла или каталога размером 1 КБ. Если это не удается, я не хочу игнорировать ошибку - это может указывать на ошибку в другом месте, аппаратный сбой и т. Д. Однако на самом деле не стоит прилагать усилий для аннотирования каждой безумно маловероятной ошибки.

Мне не нравится, как try (..) просто помещает еще одну пару круглых скобок в память программиста, чтобы думать о них при вводе. И как можно дольше представить!

Так что лучше:
значение, ошибка: = foo ()
вернуть ошибку, если ошибка! = ноль

Но все же это так часто. Поэтому я бы хотел, чтобы такое smt было как-то возможно:

значение, проверьте err: = foo ()

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

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

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

@icholy Пожалуйста, будьте вежливы. Обратите внимание на Кодекс поведения Gopher: https://golang.org/conduct. Спасибо.

Всем: в дополнение к моему комментарию выше (https://github.com/golang/go/issues/32825#issuecomment-506740412) имейте в виду https://golang.org/wiki/NoPlusOne. Бесполезно комментировать что-то большее, чем «Я согласен» или «Я не согласен». Вместо этого используйте кнопки эмодзи. Спасибо.

@sanbornm

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

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

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

(Я понимаю, что предложение B в данном случае заблокировано; тот факт, что это предложение получило 77 комментариев менее чем за день, показывает, почему. Такой уровень обсуждения просто лучше работает в списке рассылки, а не в системе отслеживания проблем.)

@ianlancetaylor

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

Достаточно честно, в этом есть смысл. Списки рассылки великолепны, но мне лично в этом случае легче внести свой вклад через GitHub. Мне нечего сказать, кроме того, что текущая обработка ошибок великолепна, и я хочу, чтобы она оставалась прежней. Эмодзи / голоса отлично подходят для этого. Вероятно, вы не хотите, чтобы 100 человек написали «Оставьте обработку ошибок в покое» в списке рассылки, где будет достаточно 100 «голосов».

Поскольку этот вопрос заблокирован, за него больше нельзя «проголосовать» с помощью Emojis. Вот почему я считаю, что эта проблема возникла в первую очередь.

Боковой момент, но связанный с этим, управление зависимостями было обработано не очень хорошо. Dep отлично поработал, и мод Go был выбран (как казалось) из ниоткуда [1]. Я так понимаю, поэтому была создана система предложений. Я просто чувствую, что система предложений в этом случае может недостаточно представлять сообщество, если проблемы заблокированы, и нас просят обращаться к спискам рассылки.

[1] https://twitter.com/_rsc/status/1022588240501661696

Изменить: команда и сообщество Go по большей части проделывают потрясающую работу, прислушиваясь к отзывам сообщества. Я ценю всю работу, вложенную в это. Опросы Go - отличный тому пример.

@sanbornm

Деп работал отлично

Здесь нужно не соглашаться. Модули Go наконец-то решили эту малоизвестную проблему «gobindata» с их постоянным кешированием на https://proxy.golang.org.

Этот начальник даже не осознавал проблемы и вместо этого играл с причудливым «обеспечением» через VCS.

@sirkon Это немного не по теме, но вам ничего из этого не нужно, если вы продаете службы, подобные Dep.

В нынешнем виде, я думаю, я бы предпочел оставить все как есть, если не было добавлено больше ограничений, например, по одному оператору try на строку. Причина в том, чтобы рассмотреть этот пример из предложения - он кажется достаточно безобидным info := try(try(os.Open(file)).Stat()) но утечка дескрипторов файлов выходит за рамки обычного потока управления. Я думаю, что мы увидим увеличение утечек файловых ресурсов с реализациями io.Closer или другой функцией очистки, от которой люди могут уклониться в погоне за более компактным кодом.

Возможно, некоторые люди сочтут это несущественным, потому что f больше не будет жить и, следовательно, будет иметь право на сборку мусора немедленно, и в какой-то момент финализатор обеспечит закрытие f. Я думаю, что это меняет предыдущие четкие (поддерживаемые линтером) соглашения об использовании defer сегодня, которые привязаны к функциональному блоку. При выходе из функционального блока ресурс освобождается. Использование сборщика мусора не дает никаких гарантий, что вы не исчерпаете ресурсы (типичные значения ограничения дескриптора открытого файла по умолчанию могут быть от 1 до 4 КБ), что легко превышается с помощью простого пути к файлу.

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

var n int
for _, name in try(os.Readdir(...)) {
   n += try(getSize(name))
}
func getSize(name string) (int, error) {
   return try(try(os.Open(name)).Stat()).Size
}

редактировать:
Что касается ограничений, я на самом деле думаю, что если бы оно было действительным только в правой части присваивания, это было бы лучше, чем говорить 1 на строку, поскольку a, b := try(get("a")), try(get("b")) достаточно разумно. Но он по-прежнему оставляет возможность делать try(os.Open(name)).Stat() - что, если бы вы сделали try () недействительным, но только когда не на правой стороне назначения, у вас останется что-то, что не очень функционально, например, все.

@cstockton вау отличная находка!

В Rust на самом деле есть похожий макрос ( ? если я правильно помню), который делает именно то, что должен был делать этот try , но у них там правильный raii, так что это не проблема на этом языке и огромная дыра в нашем случае

@sanbornm да, оставить половину интернета в репо - действительно отличная идея.

Finishing Touch

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

| Проект | LOC * | попробовать кандидатов |
| ---------- | ------ | ---------------- |
| cal1 | 2047 | 3 |
| насос1 | 1030 | 0 |
| docs1 | 4576 | 8 |
| hugoutil | 604 | 1 |
| все остальное | 8452 | 23 |

  • Только код Go, без комментариев, согласно утилите cloc .

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

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

Самая большая причина, по которой я люблю это, заключается в том, что его спецификация ограничивает кодировщиков небольшим подмножеством синтаксиса, доступным для других языков. Поскольку это такой небольшой набор функций, легко изучить весь набор функций. Будущий разработчик, вероятно, сможет взглянуть на мой код и узнать, что я сделал. Каждая новая вещь, добавляемая к языку, снижает вероятность того, что будущий разработчик знает об этом. Крайняя часть скользкой дорожки - это язык, сложность которого затрудняет понимание, например C ++ или scala.
Я не хотел бы видеть никаких синтаксических дополнений в go 1. Вместо этого поместите их в go 2.

@miekg, пожалуйста, добавьте эту ссылку https://github.com/golang/go/issues/32825#issuecomment -506882164 в предложение. Пример полностью исключает всю идею этого недавнего ключевого слова try .

image

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

Если бы я мог просто уменьшить

if err := foo.x(a, b); err != nil {
  return err
}

if err := foo.y(); err != nil {
  return err
}

if err := foo.z(c); err != nil {
  return err
}

к чему-то вроде

if err := check foo.x(a, b), foo.y(), foo.z(c); err != nil {
  return err
}

может быть , тоже было бы здорово, не меняя слишком много идиомы ИМХО.

Если вы говорили о типе «Может быть», сначала требуется вариантный тип.

Давайте оставим if err != nil это работает, это ясно, это действительно не так многословно, и это имеет смысл в потоке кода. Читая код с этой конструкцией, вы знаете, что она будет делать.
Оставим, не будем добавлять try

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

Для меня приемлемы 3 буквы «err» на одном уровне. Я не хотел бы, чтобы какая-то функция проверки обернула важный код, потому что важный код будет на втором уровне (помните лисп?), И я не хотел бы, чтобы строка `` попробовала '' раньше, потому что важный код будет с отступом и на второй строке.

res, err: = begin_job ()
if err! = nil {
handle_error ()
}

err = continue_job (res)
if err! = nil {
handle_error ()
}

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

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

Project LOC * попробовать кандидатов
cal1 2047 3
насос1 1030 0
docs1 4576 8
hugoutil 604 1
все остальное 8452 23

  • Только код Go, без комментариев, согласно утилите cloc .

Я думаю, что try больше нужны в больших программах. Просто рисуя из памяти, я думаю, что программы с размером LOC около 15-20k и выше нуждаются в них больше, потому что именно тогда вы можете начать получать слои, которые просто должны передавать ошибки, потому что они точно определены и обрабатываются в закрытой системе с помощью как отправляющая, так и получающая сторона. Однако это во многом зависит от того, какая это программа. Я бы, вероятно, не стал бы использовать много проб в небольших программах

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

Хорошая точка зрения. Я попробовал tryhard на heptio / contour, 28.7k строк исходного текста, tryhard нашел 12 замен.

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

Хорошая точка зрения. Я попробовал tryhard на heptio / contour, 28.7k строк исходного текста, tryhard нашел 12 замен.

УХ ТЫ! 12 против 28,7 тыс. Строк, для этого действительно нужно специальное ключевое слово!

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

stat := try(try(os.Open(fileName)).Stat())

Я думаю, что это более распространено, если ваша программа немного более монолитна и не является частью интеграции служб между многими службами. Когда я просто ищу fmt.errorf или errors в github в этом репозитории ( heptio/contour ), результатов очень мало, поэтому сложно получить быстрый обзор .. Но как я сказал, что это, вероятно, сильно варьируется от программы к программе, даже для более крупных программ.

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

Я думаю, что это более распространено, если ваша программа немного более монолитна и не является частью интеграции служб между многими службами. Когда я просто ищу fmt.errorf или errors в github в этом репозитории, результатов очень мало, поэтому сложно получить быстрый обзор .. Но, как я уже сказал, это, вероятно, сильно отличается от программы программировать даже для больших программ.

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

Вы не понимаете. Аннотации позволяют легко определить причину возникновения ошибки. Точно так же у нас есть os.NotExist но это едва ли хороший намек на путь ошибки.

@thomasf вот еще одна точка данных из рабочей копии

529628 строк исходного кода, трихард нашел 1763 (0,3%) замены.

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

529628 строк исходного кода, трихард нашел 1763 (0,3%) замены.

Как мудро сказал кто-то (необходима цитата), try не упрощает обработку ошибок. С ними легче не обращаться.

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

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

а. есть много мест, где try применимо к существующим базам кода go
б. Обработка ошибок в целом составляет значительную часть SLOC, основываясь на моих собственных измерениях и цифрах, поставленных командой Go, ссылка https://youtu.be/RIvL2ONhFBI?t=440 timecode 07:26

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

  1. Вы делаете оценочные суждения о теоретическом коде, о котором ничего не знаете, что не является хорошей привычкой.
  2. Я не понимаю, почему try легче использовать не по назначению, чем то, что у нас есть сейчас, go не имеет функций для принудительной обработки ошибок, и его очень легко уже пропустить. Для меня try - это упрощение чтения кода, ЕСЛИ ему не нужно обрабатывать ошибки.

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

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

var v1, v3 string
if err := func() error {
    try(onething())
    v = try(twothing())
    try(otherthing())
    v3 = try(somethingg())
}(); err != nil {
  ... handle error...
}

Я думаю, что на этом этапе было бы неплохо написать веб-сайт, чтобы хранить данные для tryhard в разных пакетах и ​​визуализировать их. Может быть, немного измените golang / gddo (godoc.org), чтобы сделать эту работу.

Я предпочитаю оставить if err != nil покое. Но если нам нужно добавить что-то для обработки ошибок, вот новое предложение, которое добавляет для этого ключевое слово throws .

32852

Не повторяя некоторые из уже изложенных здесь аргументов, я повторяю мысль оставить if err != nil как есть.

Я могу предложить следующую точку зрения: как человек, обучавший Go сотням новичков (как программированию, так и Go с других языков), if err != nil никогда не был для них проблемой. Опытные программисты на моих семинарах сначала находят это необычным, но быстро начинают любить явный характер обработки ошибок в Go.

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

Go идеален по многим причинам. Главный из них - «if err! = Nil». Это может показаться многословным, но для людей, обучающихся программированию, это упрощает отладку кода и исправление кода.

@davecheney

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

а. есть много мест, где try применимо к существующим базам кода go
б. Обработка ошибок в целом составляет значительную часть SLOC, основываясь на моих собственных измерениях и цифрах, поставленных командой Go, ссылка https://youtu.be/RIvL2ONhFBI?t=440 timecode 07:26

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

Вот пример:

llorllale:~/go/src/github.com/hyperledger/fabric$ cloc --exclude-dir=vendor .
    2406 text files.
    2256 unique files.                                          
    3130 files ignored.

http://cloc.sourceforge.net v 1.60  T=6.69 s (272.8 files/s, 58350.9 lines/s)
--------------------------------------------------------------------------------
Language                      files          blank        comment           code
--------------------------------------------------------------------------------
Go                             1751          54365          34149         294005
YAML                             35            547           2171           2060
Bourne Shell                     26            354            325           1312
make                              3            135             96            418
CSS                               1             40             14            140
HTML                              3              7              5             63
Python                            1             50            103             57
Bourne Again Shell                1              1              6             50
Java                              3              7              4             26
XML                               2              1              4              2
--------------------------------------------------------------------------------
SUM:                           1826          55507          36877         298133
--------------------------------------------------------------------------------
llorllale:~/go/src/github.com/hyperledger/fabric$ tryhard -l . | grep -v vendor | less | wc -l
1417

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

if err != nil {
   return errors.Wrap(err) 
} 

...
if err != nil {
   return errgo.Notef(err, "error doing x") 
} 

об этом tryhard не сообщит.

У нас в компании есть такой съезд. Выполнение простого поиска и замены для возврата к исходным сообщениям об ошибке дает мне следующие результаты:

Language                             files          blank        comment           code
---------------------------------------------------------------------------------------
Go                                    2488          40317          15901         297038

tryhard сообщает о 2736 заменах, но выполнение ручного обзора оставшейся упаковки выглядит так, как будто это занижается примерно на 1850, поэтому я бы оценил в общей сложности ~ 4500 try использований в нашей кодовой базе из 300 тыс. строк.

(Лично я поддерживаю текущую явность обработки ошибок и не возражаю против этого.)

Например, если соглашение вашей компании
[переносить ошибки в собственное сообщение]
об этом tryhard не сообщит.

В этом суть - предложение try только упрощает if err != nil return err голые возвраты, оно не поддерживает перенос ошибок с помощью настраиваемого сообщения и контекста.

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

В сегодняшнем Go, если у меня есть функция с такой подписью:

func add(x, y string) (int, error)

Где-то в функции мне пришлось бы написать:

func add(x, y string) (int, error) {
    // ...
    if err != nil {
        return 0, err
    }

Заставляет писателя повторять одни и те же нулевые значения во всей функции.

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

func add(x, y string) (int, error) {
    // ...
    if err != nil {
        return ..., err
    }
    // ...
}
func main() {
    add("8", "beep") // returns 0, error(`strconv.ParseInt: parsing "beep": invalid syntax`)
}

Из опыта работы с большим количеством кода, который взаимодействует с запросами и вызовами БД, я могу сказать, что необходимость повторять нулевые значения во всех функциях - единственный минус обработки ошибок в стиле Go. В противном случае я согласен с мнением этого предложения: оставьте if err != nil покое!

Примечание: да, именованные возвращаемые значения могут _в некоторой степени_ выполнить это (https://play.golang.org/p/MLV8Y52HUBY), но после реализации нескольких функций в моих собственных кодовых базах с использованием этой техники мне напомнили, сколько фута - возвращаемые значения с именем пистолета: Я всегда закрываю указанное возвращаемое значение.

Например, если соглашение вашей компании
[переносить ошибки в собственное сообщение]
об этом tryhard не сообщит.

В этом суть - предложение try только упрощает if err != nil return err голые возвраты, оно не поддерживает перенос ошибок с помощью настраиваемого сообщения и контекста.

Да, я думал о варианте, позволяющем добавить описательную строку. Подавляющее большинство (~ 4000/4500) наших возвращаемых ошибок - это неконтекстные errgo.Mask(err) , которые я считал эквивалентными try() без описания, но в настоящее время это будет сокращение функциональность, так как errgo добавляет информацию о стеке, а try - нет (пока).

@ianlancetaylor есть предложение. @miekg предлагает вам, как одному из лидеров нашего языка, больше не пытаться заменять if err != nil какой-либо другой конструкцией, которая противоречит духу обработки ошибок, как было решено исходными авторами Go. Лично мне кажется, что вы пытаетесь доказать несущественность этого запроса, перемещая его в golang-nuts вместо того, чтобы рассматривать его, как другие наши предложения. Возможно, это не было вашим намерением, но я чувствую влияние.

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

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

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

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

Тема уже обсуждалась и в группе Google.

Текущая версия # 32437 неудовлетворительна. Встроенная функция try () скрывает множество путей выполнения от неподготовленного глаза. Первоначальное предложение с проверкой и дескриптором было очень понятным, и ключевое слово проверки выделялось.

Теперь встроенная функция try () выглядит как функция - не очевидно, что она может изменять поток управления. У нас также есть panic (), но он (я считаю) всегда в отдельной строке, имеет заметное имя и его редко используют. try (), с другой стороны, может скрываться внутри сложного выражения.

@theckman Роберт разработал первые версии Go вместе с Робом и Кеном, а Роберт и Расс присоединились к команде раньше. Они работают над Go с самого начала. Я думаю, мы можем доверять им, чтобы узнать, "противоречит ли предложение духу обработки ошибок, принятому первоначальными авторами Go".

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

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

Наш метод обработки ошибок уникален

Обработка ошибок в Rust концептуально аналогична тому, что мы делаем в Go (ошибки - это значения, явный поток управления, за исключением того, что мы используем несколько возвращаемых значений, когда вместо них используются типы суммы). У Rust была та же проблема, что и у Go, с подробной обработкой ошибок. Это подтолкнуло Rust к тому, чтобы попробовать! макрос, и в конечном итоге? оператор. Я бы сказал, что сообщество Rust даже строже, чем сообщество Go, в отношении обработки ошибок (RFC и обсуждения по обработке ошибок поучительны). Они нашли способ уменьшить количество подробностей обработки ошибок без скользкого пути плохой обработки ошибок. Я уверен, что мы тоже сможем.

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

О чем ты говоришь? Go постоянно совершенствуется. Удивительно иметь доступ к такому прекрасному языку, инструментам и документации бесплатно (как в случае свободы слова).

@theckman Роберт разработал первые версии Go вместе с Робом и Кеном, а Роберт и Расс присоединились к команде раньше. Они работают над Go с самого начала. Я думаю, мы можем доверять им, чтобы узнать, "противоречит ли предложение духу обработки ошибок, принятому первоначальными авторами Go".

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

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

Наш метод обработки ошибок уникален

Обработка ошибок в Rust концептуально аналогична тому, что мы делаем в Go (ошибки - это значения, явный поток управления, за исключением того, что мы используем несколько возвращаемых значений, когда вместо них используются типы суммы). У Rust была та же проблема, что и у Go, с подробной обработкой ошибок. Это подтолкнуло Rust к тому, чтобы попробовать! макрос, и в конечном итоге? оператор. Я бы сказал, что сообщество Rust даже строже, чем сообщество Go, в отношении обработки ошибок (RFC и обсуждения по обработке ошибок поучительны). Они нашли способ уменьшить количество подробностей обработки ошибок без скользкого пути плохой обработки ошибок. Я уверен, что мы тоже сможем.

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

О чем ты говоришь? Go постоянно совершенствуется. Удивительно иметь доступ к такому прекрасному языку, инструментам и документации бесплатно (как в случае свободы слова).

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

@ianlancetaylor есть предложение. @miekg предлагает вам, как одному из лидеров нашего языка, больше не пытаться заменять if err != nil какой-либо другой конструкцией, которая противоречит духу обработки ошибок, как было решено исходными авторами Go. Лично мне кажется, что вы пытаетесь доказать несущественность этого запроса, перемещая его в golang-nuts вместо того, чтобы рассматривать его, как другие наши предложения. Возможно, это не было вашим намерением, но я чувствую влияние.

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

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

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

Они не могут сделать ничего серьезного с нынешней системой шрифтов 60-х годов. Им нужно наконец позаимствовать идеи 80-х в Go 2.0.

О чем ты говоришь? Go постоянно совершенствуется. Удивительно иметь доступ к такому прекрасному языку, инструментам и документации бесплатно (как в случае свободы слова).

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

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

Я только что открыл предложение для встроенного оператора if: https://github.com/golang/go/issues/32860

Ссылка: https://github.com/golang/go/issues/32825#issuecomment -506707539

Насколько лучше стал бы этот мир, если бы каждый, представивший свое предложение о любой новой функции golang 2.0, которую они так хотели бы иметь, также предоставил бы ветку своей вилки https://github.com/golang/go (и любой другой необходимы репозитории), который реализует это предложение.

Вы не согласны?

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

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

как насчет этого синтаксиса:

# call error handler
try callFunction(), errorHandler()

# error handler with anonymous function
variable := try callSomething(), func(err *Error) { # error handling }

@theckman Прошу прощения, если мне кажется, что мое предложение перенести это обсуждение в другое место кажется несущественным. Я объяснил свои причины в своем запросе, и я считаю, что они все еще остаются в силе. Команда Go рассматривает обсуждения в списках рассылки, а также предложения.

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

Я полностью согласен с этим предложением, я думаю, что единственное, что нужно изменить, это просто go fmt, make go fmt, чтобы разрешить одну строку if.

Я действительно хочу одну строчку

if err != nil { return wrappedErr{err} }

вместо трех строк

if err != nil {
    return wrappedErr{err}
}

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

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

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

Это будет только «напрасная трата усилий» для [... _описание на полностью подходящем языке, опущенном для краткости_ ...].

Однако для кодера это было бы тривиальным, но полезным упражнением и, в то же время, служением сообществу Go.

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

Несмотря на то, что это конкретное предложение исходит от @griesemer , мне трудно поверить, что он десять лет

Чего бы это ни стоило, мое личное подозрение состоит в том, что процесс рассуждений команды Go по этому поводу выглядел примерно так:

  1. Go очень популярен и широко используется, поэтому у тысяч людей, естественно, есть комментарии, предложения и жалобы на него.
  2. Вы можете только так долго препятствовать. Команда Go чувствует огромное давление, чтобы что-то сделать со всем шумом, исходящим от сообщества.
  3. Это что-то.

как насчет добавления функции catch () в defer, чтобы поймать, если попытка обнаружила некоторую ошибку, такую ​​как recovery ().
пример:

func doSomthing()(err error){
    //return error
}

func main(){
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    try doSomthing()
}

по многим функциям

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

   try{
    file1 := open("file1")
    defer file1.Close()
    file2 := open("file2")
    defer file2.Close()
   }
   //without try
    file3,err := open("file3")
    defer file3.Close()
 }

как насчет добавления функции catch () в defer, чтобы поймать, если попытка обнаружила некоторую ошибку, такую ​​как recovery ().
пример:

func doSomthing()(err error){
    //return error
}

func main(){
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    try doSomthing()
}

по многим функциям

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

   try{
  file1 := open("file1")
  defer file1.Close()
  file2 := open("file2")
  defer file2.Close()
   }
   //without try
    file3,err := open("file3")
    defer file3.Close()
 }

Как это помогает обрабатывать каждую ошибку по-отдельности?

Некоторые пояснения:

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

  2. Я согласен с @ianlancetaylor, что это обсуждение лучше провести в другом месте (golang-nut). Здесь нет никаких предложений.

  3. В самом деле, @bitfield , у меня нет «внутреннего гнева по поводу многословия развернутых возвратов ошибок в Go» , спасибо :-) Но я действительно думаю, что проверка ошибок более подробна, чем это, возможно, необходимо; и тот факт, что это же мнение неоднократно высказывалось сообществом, является четким показателем того, что мы (команда Go) не одиноки в этом убеждении. Я бы не стал говорить, что есть большое давление, чтобы сделать что-то - мы работаем над этим то и дело, уже давно, и мы вполне довольны тем, что дождемся «правильного» подхода. .

Предложение try - это самое минимальное решение, которое мы нашли (при значительной помощи со стороны сообщества), которое устраняет проблему проверки ошибок. Предложение try очень ясно говорит о том, что оно _ вообще не поможет_, если каждый тест на ошибку требует обработки ошибки каким-то определенным образом. try помогает только тогда, когда все ошибки в функции проверяются и обрабатываются одинаково (а затем мы рекомендуем использовать defer ) или когда они просто возвращаются. Здесь трудно быть более точным, но давайте повторим то, что говорится в try не поможет во всех сценариях ошибок. Помогает в значительном количестве случаев. Для всего остального используйте операторы if .

@griesemer try слишком подвержен ошибкам: в Go нет RAII, поэтому во многих случаях мы не можем просто оставить функцию.

@sirkon , я не уверен, какое отношение RAII имеет к этому обсуждению. try заменяет существующие шаблоны if ..., err := f(); err != nil { return ..., err } на ... := try(f()) . Если произошла ошибка с освобождением ресурсов при использовании try , то она определенно существовала и раньше. Введение try не улучшает и не предотвращает ошибку освобождения ресурсов.

@sirkon , я не уверен, какое отношение RAII имеет к этому обсуждению. try заменяет существующие шаблоны if ..., err := f(); err != nil { return ..., err } на ... := try(f()) . Если произошла ошибка с освобождением ресурсов при использовании try , то она определенно существовала и раньше. Введение try не улучшает и не предотвращает ошибку освобождения ресурсов.

Прочтите ветку, там был пример:

info := try(try(os.Open(fileName)).Stat())

@sirkon Я видел этот пример несколько раз. Согласен, это интересно. Но давайте подумаем об этом немного подробнее. Предлагаемая встроенная функция try - это, по сути, синтаксический сахар для определенного типа шаблона, найденного в коде Go. Таким образом, мы можем преобразовать этот пример в исходный код.

    f, err := os.Open(fileName)
    if err != nil {
        return err
    }
    info, err := f.Stat()
    if err != nil {
        return err
    }

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

[Похоже, @ianlancetaylor меня опередил.]

@sirkon Эта ошибка уже возможна, try или нет - Go не мешает вам писать плохой код. Или наоборот, использование плохого кода в качестве причины, по которой нельзя разрешать try не является убедительным аргументом. Вместо этого go vet должен отмечать проблемные случаи.

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

f := try(os.Open(filename))
defer f.Close()
info := try(f.Stat())

сравните это с:

f, err := os.Open(filename)
if err != nil {
   return ..., err
}
defer f.Close()
info, err := f.Stat()
if err != nil {
   return ..., err
}

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

defer errd.Wrap(&err, "failed to do X for %s", filename)
f := try(os.Open(filename))
defer f.Close()
info := try(f.Stat())

используя что-то вроде пакета errd (см. проблему № 32676).

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

defer errd.Wrap(&err, "failed to do X for %s", filename)
f, err:= os.Open(filename)
try(err) // check is so much a better term
defer f.Close()
info, err := f.Stat()
try(err)

Кроме того, этот подход к handle выглядит здесь отлично, но разве множественные задержки не создадут беспорядок? Или будет что-то вроде синхронизации с функциональной областью (я не видел разъяснений по проблеме errd)? Если да, будет ли анонимным функциям предоставлена ​​собственная область видимости? И слежка ... ээш - кто первый, что второй?

Все это похоже на то, что в конечном итоге будет два «режима» написания кода Go. Скажите, что это не так.

@griesemer Хотя вы правы, ошибка возможна и сегодня, но я твердо

Если это предложение будет принято, они найдут способ избежать шаблонного кода if err , что позволит им писать код знакомым им способом. За исключением того, что у них будет почти десятилетний код Go в ответах на stackoverflow, сообщениях в блогах и т. Д., Которые были написаны до создания try. Они быстро научатся просто отбрасывать операторы err и if с помощью try. Им нужен размер файла, в который они могут вставлять код, заключенный в try до тех пор, пока они не смогут получить доступ к полю по своему желанию, точно так же, как Stat() в предложении попробовать. Это шаблон, к которому они привыкли, поэтому вполне естественно, что они применяют его при написании Go. Учитывая, что Go OS обрабатывает все как файл, можно предположить, что утечек ресурсов будет больше.

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

Тем не менее, как я уже упоминал в https://github.com/golang/go/issues/32825#issuecomment -506882164, я действительно поддержал бы try если бы была внесена пара небольших корректировок, я думаю, что изменение будет приветствовал дополнение к языку. Я думаю, что все, что нужно для попытки, - это сделать его действительным только на правой стороне присваивания и не позволять возвращаемому значению быть адресуемым. Сделайте «хорошие» примеры использования try (обычно по одной попытке на строку) «единственным» способом использования try, т.е.

info := try(try(os.Open(filename)).Stat()) // compiler error
f := try(os.Open(filename)) // valid
// we were forced to assign f, so we still have an identifier to Close (serve linters and users alike)
defer f.Close()
info := try(f.Stat())
a, b := try(strconv.Atoi("1")), try(strconv.Atoi("2")) // also valid 
a, b := try(strconv.Atoi("1"), strconv.Atoi("2")) // maybe?
a, b := try strconv.Atoi("1"), strconv.Atoi("2")

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

total := try(try(os.Open(filename)).Stat()).Size() + try(strconv.Atoi(try(ioutil.ReadAll(os.Stdin))))

Но _we_ будет думать о худшем, если вы позволите нам.

@daved Размещение try(err) во второй строке полностью поддерживается существующим предложением try : try просто хочет аргумент, который оценивает одно или несколько значений, где последнее значение имеет введите error , что, естественно, будет удовлетворено, когда вы напишете try(err) .

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

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

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

@griesemer Спасибо за разъяснения. В примере кода, если первая строка была var err error , может ли перенос повлиять на обе отмеченные ошибки? Я видел разговоры о том, что слежка - это проблема, с которой можно будет разобраться в будущем. Как это связано с этим?

как насчет добавления функции catch () в defer, чтобы поймать, если попытка обнаружила некоторую ошибку, такую ​​как recovery ().
пример:

func doSomthing()(err error){
    //return error
}

func main(){
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    try doSomthing()
}

по многим функциям

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

   try{
    file1 := open("file1")
    defer file1.Close()
    file2 := open("file2")
    defer file2.Close()
   }
   //without try
    file3,err := open("file3")
    defer file3.Close()
 }

Как это помогает обрабатывать каждую ошибку по-отдельности?

как и другие пользователи, совершившие

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    file1 :=try open("file1")
    defer file1.Close()
    file2 :=try open("file2")
    defer file2.Close()

        //without try
       file3,err := open("file3")
       defer file3.Close()
 }

@daved В этих примерах предполагалось, что err - это имя результата error . try всегда будет устанавливать эту переменную ошибки результата, независимо от имени (или отсутствия имени). Если у вас есть локальная переменная с именем err тогда это другая переменная. Если вы хотите сослаться на ошибку результата, у нее должно быть другое имя. Обратите внимание, что это уже неверно:

func f(...) (..., err error) {
   var err error // << err already declared
   ...

С другой стороны, если вы напишете:

func f(...) (..., err error) {
   a, b, ... err := g() // redeclaration of err
   ...

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

PS: Вероятно, нам следует перестать использовать эту проблему для обсуждений try и вернуться к исходному предложению - оно будет разблокировано и снова открыто для обсуждения завтра (1 июля).

@godcong Функция catch() (или аналогичная) позволит вам только получить ошибку, а не установить ее (и обычно нужно установить ошибку включающей функции в отложенной функции, работающей как обработчик ошибок) . Его можно заставить работать, заставив catch() вернуть *error которое является адресом возвращаемого значения ошибки включающей функции. Но почему бы просто не использовать имя результата ошибки вместо добавления нового механизма в язык? См. Также предложение try где это обсуждается.

Также см. PS выше .

@griesemer

Здесь трудно быть более точным, но давайте повторим то, что говорится в предложении: try не поможет во всех сценариях ошибок. Помогает в значительном количестве случаев. Для всего остального используйте операторы if.

Я думаю, что это как раз фатальный недостаток предложения try() : если раньше был один и только один способ проверки ошибок, то теперь их будет два, переплетенных по всей базе кода. Кроме того, по крайней мере, для базы кода, над которой я работаю, менее 20% от if err != nil можно заменить на try() , что, хотя и немаловажно, не кажется стоящим достаточно для создания разделить стили обработки ошибок.

Лично я предпочел бы конструкцию обработки ошибок, которая была бы достаточно мощной, чтобы заменить 95% всех случаев if err != nil . Думаю, это бы тоже понравилось многим.

@griesemer Я согласен с тем, что люди будут учиться, и инструменты станут обязательными, поскольку руководства по стилю, передовые методы, примеры, документация и т. д., на которые вы ссылаетесь, будут устаревшими. Я думаю, очевидно, что попытка, предлагаемая в настоящее время, вводит тонкие новые способы написания неправильного программного обеспечения. Что неясно, так это то, как отклонение этого факта на основании предположения, что люди всегда могут писать неправильное программное обеспечение, является действительным контраргументом?

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

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

@sirkon Я видел этот пример несколько раз. Согласен, это интересно. Но давайте подумаем об этом немного подробнее. Предлагаемая встроенная функция try - это, по сути, синтаксический сахар для определенного типа шаблона, найденного в коде Go. Таким образом, мы можем преобразовать этот пример в исходный код.

    f, err := os.Open(fileName)
    if err != nil {
        return err
    }
    info, err := f.Stat()
    if err != nil {
        return err
    }

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

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

@godcong Функция catch() (или аналогичная) позволит вам только получить ошибку, а не установить ее (и обычно нужно установить ошибку включающей функции в отложенной функции, работающей как обработчик ошибок) . Его можно заставить работать, заставив catch() вернуть *error которое является адресом возвращаемого значения ошибки включающей функции. Но почему бы просто не использовать имя результата ошибки вместо добавления нового механизма в язык? См. Также предложение try где это обсуждается.

Также см. PS выше .

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

Будет интересно посмотреть, в какой степени новые и улучшенные средства устранения ошибок будут использоваться в самом golang/go . Если вообще.

Также будет интересно посмотреть, будет ли go2 fmt иметь возможность выводить простой go1.x .

По моему собственному опыту, с тех пор, как я добавил контекст в свою возвращенную ошибку:

import "github.com/pkg/errors"
func caller(arg string) error {
  val, err := callee(arg)
  if err != nil {
    return errors.Warpf(err, "failed to do something with %s", arg)
  }

  err = anotherCallee(val)
  if err != nil {
    return errors.Warpf(err, "failed to do something with %s", val)
  }

  return nil
}

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

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

Возможно, добавьте контекст в defer:

func caller(arg string) (err error) {
  defer func() {
    switch t := err.(type) {
      case CalleeErr:
        err = errors.Wrapf(err, "failed to do something with %s", arg)
      case AnotherCalleeErr:
        err = errors.Wrapf(err, "failed to do something with %s", val)
    }
  }()

  val := try(callee(arg))
  try(anotherCallee(val)
  return nil
}

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

Может закончиться использование try () таким образом:

func caller(arg string) error {
    val, err := callee(arg)
    try(errors.Warpf(err, "failed to do something with %s", arg))

    err = anotherCallee(val)
    try(errors.Warpf(err, "failed to do something with %s", val))

    return nil
  }

Он сокращает несколько строк, вот и все.

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

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

return err if err != nil

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

как насчет добавления функции catch () в defer, чтобы поймать, если попытка обнаружила некоторую ошибку, такую ​​как recovery ().
пример:

func doSomthing()(err error){
    //return error
}

func main(){
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    try doSomthing()
}

по многим функциям

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

   try{
  file1 := open("file1")
  defer file1.Close()
  file2 := open("file2")
  defer file2.Close()
   }
   //without try
    file3,err := open("file3")
    defer file3.Close()
 }

Как это помогает обрабатывать каждую ошибку по-отдельности?

как и другие пользователи, совершившие

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

  file1 :=try open("file1")
  defer file1.Close()
  file2 :=try open("file2")
  defer file2.Close()

        //without try
       file3,err := open("file3")
       defer file3.Close()
 }

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

@ianlancetaylor Кто-нибудь предлагал

  • try в качестве имени для функции было спорным, пока мы реализуем это как функцию, мы застряли, давая ей имя, которое, я не уверен, кому-то понравится на 100%.
  • try выполняет беспрецедентное количество вещей, он имеет входные данные, такие как append() и влияет на поток управления, например, panic() , занимая место вездесущего шаблона ( if err != nil ) .
  • try Вложенность является следствием решения реализовать ее как функцию, а не как добавленную стоимость, полученную в результате строгих технических дебатов.
  • try реализована как функция для поддержания обратной совместимости

Я думаю, что если бы мы просто выводили результаты, как мы делаем типы, все выглядело бы лаконично и чувствовалось бы более «вперёд», например:

f, err := os.Open(filename)
if err != nil {
   return ..., err
}
defer f.Close()

info, err := f.Stat()
if err != nil {
   return ..., err
}

_Вставьте сюда правильное описание этого поведения в компьютерной науке_, а пока соглашайтесь на предполагаемые результаты с помощью коротких объявлений переменных :

f := os.Open(filename)
defer f.Close()
info := f.Stat()

Что выглядит намного аккуратнее, чем:

f := try(os.Open(filename))
defer f.Close()
info := try(f.Stat())

Это решает все проблемы, которые я перечислил выше, при этом (на мой взгляд) чувствуя себя немного более «похожим» и сохраняя обратную совместимость. Это также кажется немного проще для объяснения, «неявный» возврат вызова функции для try() кажется действительно неуместным, учитывая повсеместное значение try во всех других языках. Я не могу говорить о реализации на 100%, но похоже, что это можно сделать примерно с такими же усилиями? В AssignStmt может быть добавлено поле, которое указывает, какие выражения в LHS опускают свои значения ошибок, чтобы информировать ту же внутреннюю компиляцию, что и встроенную?

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

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

func example() (foo int, err error) {
    on err != nil {
        return foo, err
    }

    foo, err = calcThis()
    foo, err = calcThat(foo)

    return
}

Это также работает без объявления имен для возвращаемых значений в сигнатуре функции.

func example() (*int, error) {
    var err error

    on err != nil {
        return nil, err
    }

    foo, err = calcThis()
    foo, err = calcThat(&foo)

    return &foo, nil
}

Это также можно было установить несколько раз. Вот надуманный пример:

func example() (*int, error) {
    var err error

    on err != nil {
        return nil, err
    }

    foo, err = calcThis()

    on err != nil {
        return &foo, err
    }

    foo, err = calcThat(&foo)

    return
}

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

Вы даже можете сделать следующее:

func example() (foo int, err error) {
    var message string

    on err != nil {
        return foo, errors.Wrap(err, message)
    }

    message = "failed to calc this"
    foo, err = calcThis()

    message = "failed to calc that"
    foo, err = calcThat(foo)

    return
}

Наконец, это применимо не только для обработки ошибок. Хотите вернуть, если foo == 0? Еще один надуманный пример:

func example(i int) bool {
    on x == 0 {
        return false
    }

    x = calcSomeInt(i)
    return true
}

@cstockton

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

  • Это уже "ошибка" с функциями, которые возвращают _only_ ошибки, например json.Unmarshal . Хорошие обозреватели кода IME учатся замечать это довольно быстро.
  • Это все равно будет синтаксической ошибкой в ​​функциях, которые не возвращают значения ошибок, например в методах http.Handler .
  • Если бы вы знали, как справиться с ошибкой, вы бы подумали об этом, вы бы ее предвидели и, вероятно, все равно зафиксировали бы значение ошибки, _так_ что_ вы могли бы с ней справиться.
  • Если ваше намерение состояло в том, чтобы просто передать любые обнаруженные ошибки, чтобы сделать их проблемой кого-то другого (вызывающего), это выполнит это, с небольшим недостатком, что вы упустите возможность явно добавить больше аннотаций для контекста.

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

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

01.07.19 Крис Стоктон на [email protected] написал:

@ianlancetaylor Кто-нибудь предлагал
"предполагаемые возвраты" - в основном ведут себя точно так же, как try без функции.
вызов. [...]

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

Лючио.

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

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

data := try(ioutil.ReadAll(try(os.Open("foo.txt"))))

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

data := try(ioutil.ReadAll(try(http.Get("http://example.com/")).Body))

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

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

@ jesse-amano Использование оператора присваивания фактически предотвращает возможность этого случая, без явного оператора присваивания приведенные ниже действия ведут себя точно так же, как сегодня, то есть:

json.Unmarshal(...)
(http.Handler)(h).ServeHTTP(w, r)

Что касается значений, которые просто возвращают ошибку, могут быть возвращены как return json.Unmarshal(...) а также могут быть представлены в более компактной форме, поскольку нет необходимости в существовании значения за пределами блока if.

if err := json.Unmarshal(...); err != nIl {
    return err
} 

Что меня интригует в этом случае, так это то, что у нас есть нечто аналогичное парадигме «запятая ОК», где теперь опускание правопреемника «err» допустимо при некоторых четко определенных обстоятельствах. Стоит отметить, но явно недостаточно, чтобы сделать это предложение обоснованным.

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

edit: @ jesse-amano Я полностью упустил вашу точку зрения, извините! Я полагаю, что находясь внутри функции, которая не возвращает ошибку, будет отображаться типичная ошибка компиляции несоответствия подсчета назначений? Я полагаю, что try, вероятно, представит для этого новый тип сообщения об ошибке компилятора.

@beoran Что касается https://github.com/golang/go/issues/32825#issuecomment -507126700: Обработка ошибок уже отличается от ситуации к ситуации: иногда мы возвращаем ошибку без изменений, иногда мы возвращаем оформленную ошибку, иногда мы действуем при ошибке, и иногда мы (можем правильно) игнорировать ошибку. Единственное, что они все разделяют (кроме случаев, когда мы игнорируем ошибку), - это тест err != nil (который мы уже можем сделать более чем одним способом). Как бы хорошо ни было, чтобы новая языковая функция охватывала все эти случаи (или 95% из них), такая конструкция с большой вероятностью будет мешать неортогональным образом другим конструкциям управления, которые у нас уже есть. То есть лучшее, на что мы можем надеяться, - это улучшить некоторые из этих случаев (может быть, на 20%, может быть, на 50%).

@cstockton Относительно https://github.com/golang/go/issues/32825#issuecomment -507136111: если вложенные try - единственный оставшийся дорожный блок, а go vet недостаточно , Я думаю, мы можем рассмотреть вопрос о запрете вложенных try - это было бы достаточно просто. Но на данный момент я думаю, что мы все еще находимся в фазе FUD (страх, неуверенность и сомнения), и практически никто не экспериментировал с try серьезно. Отмечу, что люди, которые это сделали, отчитались положительно.

PS: Вопрос try снова открыт для отзывов. Продолжим оттуда.

@cstockton О, json.Unmarshal без захвата значения ошибки в большинстве случаев, но обычно не считается плохой практикой вместо defer file.Close() из defer func() { err = file.Close(); if err != nil { ... }; }() , и мы довольно легко научились отличать разницу. Так будет (вероятно) с предложенным вами изменением. Сначала я вздрогнул при мысли о том, что кто-то невинно использовал foo := strconv.ParseFloat(bar, 64) когда они намеревались обработать ошибку, но после краткого размышления я действительно не думаю, что это все-таки проблема.

@sirkon Относительно https://github.com/golang/go/issues/32825#issuecomment -507167388: Пожалуйста, не обсуждайте такие явно неквалифицированные утверждения - им здесь нет места. Для справки: многие идеи в Go на самом деле относятся к 80-м годам (пакеты, отдельная компиляция и т. Д.), А не к 60-м годам, и многие из них намного моложе (горутины, интерфейсы). Эти идеи могут показаться устаревшими, но они выдержали испытание временем (по крайней мере, то короткое время, когда существует CS).

@turtleDev Относительно https://github.com/golang/go/issues/32825#issuecomment -507231353: Go выполняет обработку исключений, и это делается с помощью panic и defer и recover - мы просто не называем это «обработкой исключений», потому что этот термин имеет значение, которое вводит в заблуждение для Go. Но для ясности: Go может делать все то же, что raise с try-catch , и многое другое (потому что, в отличие от try-catch , можно использовать defer условно). Спасибо.

@sorenvonsarvort Относительно https://github.com/golang/go/issues/32825#issuecomment -507268293: если вам нужно различное оформление ошибок для каждого случая, используйте инструкцию if . См. Проектную документацию. На этот вопрос уже много раз ответили. Спасибо.

@cstockton По поводу https://github.com/golang/go/issues/32825#issuecomment -507306652: Да, мы думали о таком механизме. В частности, мы подумали о «повороте столов» и вместо предоставления try просто предоставили handle , который объявляет обработчик ошибок. Если обработчик присутствует (и только тогда), можно просто оставить err в LHS присвоения, и это будет проверяться неявно (как с невидимым try ). Он выглядит красиво, но при этом полностью невидим, что является большим красным флагом. Мы действительно хотим, чтобы обработка исключений была явно видна в коде повсюду. Без этого было бы почти невозможно прочитать код и посмотреть, где происходит проверка ошибок.

@griesemer спасибо за разъяснения. но panic и recovery имеют разные варианты использования, и по большей части их очень трудно найти в любой производственной базе кода. это оставляет вам лишь ограниченный контроль над конструкциями потока. добавление новой конструкции нарушит эту согласованность, поскольку теперь у вас есть новый элемент управления конструкцией потока, который выполняет что-то вроде return.

@ matthew-noken По поводу https://github.com/golang/go/issues/32825#issuecomment -507323973: Вы предлагаете интересную идею; это очень похоже на механизм точки наблюдения отладчика. Есть несколько вопросов, на которые необходимо ответить: должен ли возвращаться блок on (я подозреваю, что да, потому что в противном случае вы попадете в страну спагетти-кода)? Можно ли иметь более одного такого оператора on ? Насколько сложным может быть условие on (его нужно будет оценивать при каждом назначении)? Обратите внимание, что у нас не может быть произвольных выражений, потому что мы должны однозначно идентифицировать переменную с помощью оператора on . Кроме того, в Go есть анафема: конструкция on подразумевает, что невидимый код должен выполняться где-то еще.

Если вы хотите изучить это подробнее, я предлагаю обсудить это в другом месте (golang-nut или другое, новое предложение). Спасибо.

@as Относительно https://github.com/golang/go/issues/32825#issuecomment -507345821:

арифметика указателей была исключена из Go по той причине, что она упрощает написание плохого, сломанного кода.

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

Нет ни опыта, ни свидетельств того, что вложенные try будут распространены или распространены. Но см. Https://github.com/golang/go/issues/32825#issuecomment -507358397.

@turtleDev Относительно https://github.com/golang/go/issues/32825#issuecomment -507368167: panic _exactly_ исключение, а recover внутри отложенной функции по сути является catch . Их может быть труднее найти в производственном коде, потому что в Go мы не рекомендуем писать код с использованием исключений; их следует использовать только в исключительных случаях.

Что касается количества структур потока управления: предложение очень ясно, что try - это просто синтаксический сахар, и ничего больше.

Я попытался ответить на некоторые из недавних комментариев в этой ветке этой ветки. Но давайте продолжим новые комментарии к предложению try в фактическом выпуске try № 32437 (на сегодняшний день снова разблокировано); и оставьте этот вопрос зарезервированным для обсуждения leave err != nil alone . Спасибо.

@cstockton Еще один комментарий о https://github.com/golang/go/issues/32825#issuecomment -507306652: если мы реализовали это, то начиная с

    func f() int { ... }
    ...
    x := f()

и переход на

    func f() (int, error) { ... }

будет означать, что поведение x := f() внезапно и незаметно изменится.

Я провел несколько экспериментов, подобных тому, что сделал @lpar во всех наших репозиториях go. Результаты представлены в следующем виде: https://gist.github.com/freeformz/55abbe5da61a28ab94dbb662bfc7f763

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

func f() int { ... }
func a() error {
    x := f() // if f() is updated to return an error, we get automatic error propagation by default
    ...
}

func b() {
    x := f() // if f() is updated to return an error, we get the same compiler error 
    // assignment mismatch: 1 variable but pkg.f returns 2 values
}

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

func (t *T) a() error {
   t.mu.Lock()
   x := f() // if f() is updated to return an error, we get automatic error propagation by default
   if x > 15 {
     t.mu.Unlock()
     return errors.New("t > 15")
   }
   ...
}

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

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

@griesemer

Что касается количества структур потока управления: предложение очень ясное, что try - это просто синтаксический сахар, и ничего больше.

Простите меня, но try не будет макросом (как C), поэтому, по сути, для конечного пользователя это просто еще один элемент управления оператором потока.

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

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

Сторона: Мне кажется странным, что в этом выпуске 1335 голосов "за", а в предложении try (# 32437) только 279 голосов "против". Я ожидаю, что люди, проголосовавшие за это, проголосуют против предложения try чтобы мнение сообщества по этому поводу было более очевидным, потому что эти два предложения являются взаимоисключающими.

@griesemer

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

Согласитесь, это очевидно.

Единственное, что они все разделяют (кроме случаев, когда мы игнорируем ошибку), - это тест err != nil (который мы уже можем сделать более чем одним способом). Как бы хорошо ни было, чтобы новая языковая функция охватывала все эти случаи (или 95% из них), такая конструкция с большой вероятностью будет мешать неортогональным образом другим конструкциям управления, которые у нас уже есть. То есть лучшее, на что мы можем надеяться, - это улучшить некоторые из этих случаев (может быть, на 20%, может быть, на 50%).

Предлагаемый оператор try() также "мешает" if и return неортогональным образом, поэтому я бы сказал, что этот аргумент неверен. Некоторым здесь не нравится try() по этой причине, но я не согласен. Го - это не Оберон, он простой, но не минималистичный, Го более практичен.

В чем мы не согласны, так это в том, что стоит даже возиться с языковой конструкцией, которая, как вы сами признали, имеет лишь ограниченное использование и применимость, и это уже можно сделать правильно с помощью if и return . Я думаю, что многие люди, вроде меня, которые высоко оценили эту ветку, настолько недовольны try() , что предпочли бы вообще не иметь ее. Даже если он не ортогонален return, более мощный и более полезный try() , вероятно, хотел бы видеть большинство программистов Go.

@beoran ,

Вы писали, что хотели бы "более мощный", "более полезный" try() .

@griesemer упомянул 4 ситуации:

  1. Вернуть ошибку без изменений
  2. Вернуть оформленную ошибку
  3. Действовать при ошибке
  4. Игнорировать ошибку

try() решает 1 по замыслу: это буквально ярлык для if err != nil { return ..., err } .

Существующие языковые конструкции решают 3 и 4. Мы уже можем действовать в случае ошибки с помощью if err != nil { ... } и в этом случае будет трудно найти более сжатую структуру. Мы уже можем игнорировать ошибку с помощью _ .

Остается 2 (вернуть оформленную ошибку). Предложение try() предлагает использовать оператор defer для украшения ошибки, или, если каждая ошибка должна быть оформлена по-разному, используйте стандартную конструкцию if err != nil { ... } .

Аргументация хорошо объясняется в этой части предложения :

Наша первая итерация этого предложения была вдохновлена ​​двумя идеями из Key Parts of Error Handling : использовать встроенный, а не оператор, и обычную функцию Go для обработки ошибки, а не новую языковую конструкцию обработчика ошибок. В отличие от этого поста, наш обработчик ошибок имел фиксированную сигнатуру функции func(error) error чтобы упростить задачу. Обработчик ошибок будет вызываться try при наличии ошибки, непосредственно перед тем, как try возвращается из включающей функции. Вот пример:

handler := func(err error) error {
        return fmt.Errorf("foo failed: %v", err)  // wrap error
}

f := try(os.Open(filename), handler)              // handler will be called in error case

Хотя этот подход позволил спецификацию эффективных обработчиков ошибок, определяемых пользователем, он также открыл множество вопросов, на которые не было явно правильных ответов: что должно произойти, если обработчик предоставлен, но равен нулю? Должен ли try вызывать панику или рассматривать его как отсутствующий обработчик ошибок? Что, если обработчик вызывается с ошибкой, отличной от нуля, а затем возвращает нулевой результат? Означает ли это, что ошибка «отменена»? Или включающая функция должна возвращать ошибку nil? Также было неясно, приведет ли включение дополнительного обработчика ошибок к тому, что программисты вообще будут игнорировать надлежащую обработку ошибок. Также было бы легко выполнить правильную обработку ошибок повсюду, но пропустить единственное появление try . И так далее.

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

[...]

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

Вы не согласны с этим рассуждением?

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

Кроме того, теперь мы обрабатываем все 4 варианта использования ошибок с помощью оператора if err != nil , который является широко понятной и последовательной идиомой Go. Использование только try() для случая 1 и, возможно, для случая 2, если мы не возражаем против накладных расходов на выполнение переноса ошибок в операторе defer, это означает, что код для обработки ошибок будет разделен на if и try несовместимы, и если обработка ошибок изменится, нам придется переключаться между одним и другим.

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

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

@beoran Я думаю, что случаи 1 и 2 обычно возвращают ошибку из функции (соответственно без оформления и с оформлением), а случаи 3 и 4 - нет (соответственно действуют на ошибку и игнорируют ошибку). Я думаю, что "try ()" сосредоточен на случаях 1 и 2.

Что, если предложение try() можно улучшить для обработки случаев 1 и 2, приняв дополнительную функцию обработчика? Конечно, для этого необходимо найти разумные ответы на вопросы, перечисленные в предложении, но это кажется послушным. Вы бы поддержали что-то подобное?

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

Если серьезно, если бы я был тем, кто принимал эти решения, я бы действительно хотел перепрофилировать оператор Kotlin ? или что-то вроде поведения rust unwrap() .

Я думаю, что любой из этих вариантов был бы улучшением:

getFile()?.getPath()?.toString()

где вы получите nil обратно, если в пути произошла ошибка или

get_file().unwrap().get_path().unwrap().lossy_to_string()

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

02.07.19 Николя Грилли на [email protected] написал:

@beoran Я думаю, что случаи 1 и 2, как правило, возвращают ошибку из
функции (соответственно без и с декором), а случаи 3 и 4
не делать (соответственно действовать при ошибке и игнорировать ошибку). Я думаю "попробуй ()"
основное внимание уделяется случаям 1 и 2.

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

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

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

Однако из этого следует, нравится нам это или нет, так это то, что
позиционирование "err" как последнего возвращаемого параметра скоро будет
стать законом, а не условностью. Это один из многих "непреднамеренных" или
«побочные» последствия, которые необходимо учитывать.

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

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

@lootch Вас особенно беспокоит, что ошибка должна быть последним возвращаемым параметром для работы try ? Разве это не конвенция де-факто?

(Кстати, мы не «задеты» сопротивлением - в основном ошеломлены чрезмерной реакцией.)

@griesemer извините за выглядят так: «Мы решили, что добавим либо дизельную топливную систему для всех бензиновых автомобилей, либо аккумулятор и электродвигатель для всех бензиновых автомобилей». Обработка ошибок в Go достаточно хороша. Меня беспокоит, что добавленная стоимость будет меньше затрат, включая новые ножные ружья и умственные издержки.

Я просто хочу написать небольшой комментарий, в котором говорится, что, написав много Go (я использую Go с момента его первого публичного выпуска в 2009 году - см. Мой github), я хотел бы улучшить эргономику обработки ошибок. Хотя явная обработка ошибок в Go хороша, она также является проблемой для последовательного кода. Отсутствие самоанализа и набора текста вокруг самих фактических значений (что рассматривается в другом предложении), по моему опыту, на самом деле не улучшает отказоустойчивость программы.

Меня это достаточно разозлило несколько лет назад, я на самом деле написал немного сахара вокруг паники и восстановления, чтобы позволить им работать (в основном) как непроверенные исключения: https://hackthology.com/exceptions-for-go-as-a-library. html . На самом деле использование этих оберток - плохая идея, потому что там находится сообщество. Однако я по-прежнему считаю, что улучшение эргономики обработки ошибок - это победа.

Кажется безумием, что люди страстно отстаивают чрезвычайно подробный, но бесполезный способ распространения ошибок. Я написал и нашел настоящие ошибки в своем коде (и другом коде), когда люди испортили условие if err != nil и создали ошибки. На самом деле нет никаких причин для написания этих ошибок. Статическая проверка может помочь, но не устранить эти ошибки.

Я поддерживаю предложения по улучшению эргономики обработки ошибок.

Процесс предложения, если цитировать https://golang.org/s/proposal , означает «Предложение изменений, которые нужно принять».
В этом выпуске не предлагается изменение, так что на самом деле это ошибка категории.
Если кто-то подавал предложение под названием «не трогать», мы просто закрывали его как не предложение.

Но на самом деле этот вопрос - просто продолжение обсуждения других вопросов, таких как # 32437, поэтому я оставлю его открытым как хорошее место для обсуждения «ничего не делать».

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

Сторона: Мне кажется странным, что эта проблема получила 1335 голосов "за", в то время как предложение try (# 32437) имеет _только__ 279 голосов "против".

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

02.07.19 Роберт Гриземер на [email protected] написал:

@lootch Вас особенно беспокоит, что ошибка должна быть последней?
возвращаемый параметр для работы try ? Разве это уже не де-факто
соглашение?

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

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

Я неохотно использую несколько хитрых уловок Go (у меня
где-то упоминалось, что x ++ и x-- не имеют
иногда вверх - в моем коде), так что "попробовать" останется для меня одним из них,
но я принципиально не возражаю против его введения, я просто чувствую
что было сказано так много, и все же все возможные недостатки могут не
были выявлены (непредвиденные последствия того, что я вижу,
возможное политическое решение). Проверить это может быть хорошо
или плохо.

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

Тем не менее, имеет смысл попробовать испытать себя после того, как
все, дополнительная функция. Но есть один побочный эффект.
обмен: каковы будут последствия "де-факто"
обязательные функции обработки ошибок иметь аргумент типа "ошибка"
в конце списка возвращаемых параметров? Как это изменит
представление о том, что «ошибки - это просто ценности»? Как это будет согласовано с
теперь гораздо более аналогичная парадигма "запятая-ОК"? Что еще будет это
новый принцип рождает?

Больше всего я думаю, что гора, сделанная из этой мухи слона,
Показательно, что Go достигла важной вехи в жизни. Почти
конечно, предположения, которые были верны в 2013 году, вполне могут больше не
держать. Не то чтобы это для кого-то новость, но это говорит о том, что Go2 может
быть не таким замечательным, чтобы быть обратно совместимым с Go1, как это было
слишком твердо предложено (на мой взгляд).

@lootch Спасибо за подробный ответ. Действительно, очень сложно расширить существующий язык обратно совместимым способом, и поэтому я согласен с вами, что, если бы эта обратная совместимость не требовалась, возможно, можно было бы поступить по-другому. Вскоре (когда модули станут обычным явлением) мы сможем вносить изменения в язык, которые не требуют строгой обратной совместимости.

Как вы говорите, try - это необязательный механизм, и - я думаю, стоит повторить - несмотря на всю шумиху вокруг него, очень простой механизм, который легко объяснить с помощью существующих функций Go - просто мы можем '' Мы сами пишем try как функцию в Go (и дженерики тоже не помогут). Если бы мы могли, я уверен, что довольно часто можно было бы увидеть вспомогательные функции, такие как try для обработки ошибок в существующем коде. В конце концов, это именно то, о чем говорит Роб Пайк об ошибках как о ценностях: это программирование с ошибками.

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

Еще раз спасибо за ваш вклад. Все это очень интересно.

Я не уверен, что это предложение - подходящее место для его обсуждения, но, учитывая, что в этой ветке уже есть живое обсуждение ключевого слова try, я просто рассмотрю его сейчас по теме :)

Интересно, захотят ли люди из Google реализовать ключевое слово try в своем внутреннем репозитории Golang и впоследствии преобразовать существующие базы кода Google для использования этого ключевого слова. Если оставить его только для внутреннего использования, это позволит легко вернуть его обратно (то есть нагрузка ложится на одну компанию, а не на все сообщество).

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

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

@Freeaqingme Мы уже создали предварительную CL, которая показывает, как try может выглядеть в библиотеке std: https://go-review.googlesource.com/c/go/+/182717 (может быть ложное положительные, но если и есть, то очень редко). Мы думаем, возможно, разработать инструмент, который позволит конвертировать кодовые базы в обоих направлениях.

Вам также рекомендуется использовать tryhard в своей базе кода и сообщать об этом.

Спасибо.

@griesemer Возможно, я не совсем ясно

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

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

@Freeaqingme Понятно . Конечно, мы могли бы запустить try поверх внутренних репозиториев. Я не уверен, что мы можем конвертировать и конвертировать обратно - это требует значительных затрат. Кроме того, мы могли сообщить только агрегированные (статистические) данные по этому эксперименту, поскольку мы не смогли бы сообщить о внутренних деталях. То есть у сообщества не будет простого способа проверить наши утверждения. Наконец, кодовая база Google может не быть репрезентативной.

Но спасибо, замечание принято.

@griesemer Я понимаю, что это может оказаться дорогостоящим мероприятием. В таком случае я бы не стал этого делать. Если это просто вопрос применения вашего серьезного проекта, то стоимость может быть ограничена. Но на самом деле это должен определить гуглер (а я не являюсь).

Я понимаю, что вы не сможете отчитаться по отдельным проектам Google или внутренней инфраструктуре. Однако, возможно, можно было бы поделиться несколькими анекдотами. Но что лично я считаю более ценным, так это отчеты некоторых гуглеров о том, как это сработало для них. Не вдаваясь в подробности, утверждения типа «Я ожидал X, но когда я столкнулся с такими случаями, как A и BI, обнаружил, что Y», я считаю очень ценными. Я не считаю нужным проверять такие отчеты.

Наконец, кодовая база Google может не быть репрезентативной.

Может быть, а может и нет. Но в Google работает много людей, поэтому я считаю, что большая часть кодовой базы основана не на том, как один человек решил ее написать, а, скорее, на кульминации вклада множества разных участников (сотрудников). В этом отношении я ожидаю, что все будет настолько представительным, насколько это возможно. Вероятно, не существует 100% репрезентативной базы кода.

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

Введение нового синтаксиса означает, что каждый должен обновить свою версию Go. Я все еще использую Go 1.10, потому что мой рабочий процесс основан на том факте, что я могу go get вещи, а затем использовать их из командной строки (мой GOPATH находится в моем PATH ) . Недавно у меня возникла проблема при попытке go get чужой библиотеки, которая использует модули. Я получил сообщение об ошибке .../v2 недоступно. Это означает, что код уже разделен (подумайте о Python 2 и 3). Для меня существует мир до Go 1.11 и после Go 1.11. Это очень раздражает, и введение нового синтаксиса для чего-то, что работает, а также обработка ошибок в Go - не лучший компромисс. Это приводит к большей фрагментации.

4.07.19 gonutz [email protected] написал:

Введение нового синтаксиса означает, что каждый должен обновить свой Go
версия. Я все еще использую Go 1.10, потому что мой рабочий процесс основан на том, что
что я могу go get вещи, а затем использовать их из командной строки (мой
GOPATH находится в моем PATH ). Недавно у меня возникла проблема при попытке go get
чужая библиотека, использующая модули. Я получил ошибку, что .../v2 было
недоступен. Это означает, что в коде уже есть разделение (подумайте
Python 2 и 3). Для меня существует мир до Go 1.11 и после Go 1.11.
Это очень раздражает и вводит новый синтаксис для чего-то, что работает
а также обработка ошибок в Go - это вообще не лучший компромисс. Это
вводит большую фрагментацию.

Если это вас утешит, я нахожусь в точно таком же положении по отношению к
Модули Go. Не нашла времени и возможности познакомиться
с ними, поэтому я тоже придерживаюсь Go1.10. Может, так и должно быть
опрос стоит иметь.

Лючио.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую или просмотрите его на GitHub:
https://github.com/golang/go/issues/32825#issuecomment -508372318

-
Лусио Де Ре
2 Piet Retief St
Кестелл (Восточный свободный штат)
9860 Южная Африка

Тел .: +27 58 653 1433
Сотовый: +27 83 251 5824
ФАКС: +27 58 653 1435

Я новый разработчик golang (все еще изучаю го). Я считаю, что текущая обработка ошибок - это хорошо, потому что она позволяет нам легко справляться с ошибками. Как разработчик Android, я думаю, что try-catch сложнее справиться с нашей ошибкой, чем if err != nil{ } в golang. И я считаю, что явная обработка ошибок всегда лучше, чем неявная обработка ошибок.

PS. Извините за мой язык.

leave it alone

Не сломано ....

Люблю мем, @ Daniel1984 :-)

Между прочим, предложение try действительно оставляет if err != nil покое; это просто дает вам дополнительную возможность там, где это имеет смысл.

Я считаю, что try не следует включать. О включении try :

Pro

  • Программисты уменьшают количество нажатий клавиш.
  • Программисты могут иметь сокращение для возврата из текущей функции в виде макроса.
  • Это не обязательно.
  • Это будет широко использоваться.
  • Понятно, где происходит волшебство (в отличие от Java throws ).
  • Глаза больше не тускнеют при просмотре моря чеков на nil .
  • Лучше всего работает с простыми реализациями.

Против

  • try добавляет повторяющийся метод для существующей операции.
  • Для try возврат из текущей функции является неожиданным, AKA больше волшебства.
  • Это добавляет несоответствия к проверке ошибок.
  • Программисты без опыта работы с Go этого не поймут.
  • Не меняет обработку ошибок.
  • Меньшая ясность (несоответствие между возвратом функции и значением выражения).
  • Трудно даже описать словами то, что происходит на try .

@griesemer это именно то, что мне не нравится. Все должно быть просто, я бы не хотел усложнять язык только для того, чтобы иметь два способа достижения одного и того же. Есть шаблоны, позволяющие избежать этой if err != nil многословности. https://www.ardanlabs.com/blog/2019/07/an-open-letter-to-the-go-team-about-try.html

Предложение Go2 № 32437 добавляет к языку новый синтаксис, чтобы сделать шаблон if err != nil { return ... } менее громоздким.

Существуют различные альтернативные предложения: # 32804 и # 32811, поскольку оригинальный вариант не всем нравится.

Добавим еще одну альтернативу: почему бы не оставить все как есть ?

Мне понравился явный характер конструкции if err != nil и поэтому я не понимаю, зачем нам для этого нужен новый синтаксис. Это действительно настолько плохо?

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

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

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

Лючио.

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

Вместо того:

err = myFunction ()
if err! = nil {
вернуть ошибку
}

Разрешать:

err = myFunction ()
if err! = nil {return err}

Кстати, предложение try оставляет только if err! = Nil; это просто дает вам дополнительную возможность там, где это имеет смысл.

Это точное оправдание того, как Go становится еще одним C ++, C #, Scala, Kotlin и т. Д. «Что ж, вам не нужно его использовать, если вы не хотите» - вот как создаются расширенные функции языков.

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

@deanveloper У вас есть наглядный пример того, как сложно понять поведение ошибки с помощью "try":
https://github.com/golang/go/issues/32437#issuecomment -498932961

Даже если это немного повторяется, я согласен с OP.
Кроме того, imo все предлагаемые альтернативы вносят бесполезную сложность.

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

@сходить с ума

Это означает, что код уже разделен (подумайте о Python 2 и 3). Для меня существует мир до Go 1.11 и после Go 1.11.

Я давно программист на Python, и предполагаемый «раскол», который вы упомянули в отношении модулей Go, - ничто по сравнению с катастрофой перехода с Python 2 на Python 3.

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

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

@pongsatornw

Я считаю, что текущая обработка ошибок - это хорошо, потому что она позволяет нам легко справляться с ошибками. Как разработчик Android, я считаю, что с помощью try-catch справиться с нашей ошибкой сложнее, чем с помощью команды if err! = Nil {} в golang. И я считаю, что явная обработка ошибок всегда лучше, чем неявная обработка ошибок.

Вы прочитали предложение полностью? try - это просто встроенная функция, которая помогает исключить повторение if err != nil { return ..., err } . Общая логика обработки ошибок в Go остается прежней. Он по-прежнему явный, ошибки по-прежнему являются значениями, и нет try-catch (иначе говоря, исключения).

@kroppt

  • try добавляет повторяющийся метод для существующей операции.

try - это просто факторизация повторяющегося кода. То же самое и с append . Мы можем писать это сами каждый раз, когда добавляем элементы в срез, но проще вызвать append .

  • Это добавляет несоответствия к проверке ошибок.

Вы можете управлять срезом «вручную», используя [...:...] или вы можете использовать append , в зависимости от того, что вы делаете. Нет никакого противоречия. Это просто разные инструменты для разных задач. То же самое для ошибок, с простым if err != nil { ... } или с try , в зависимости от выполняемой задачи.

  • Для try возврат из текущей функции является неожиданным, AKA больше волшебства.

Это неожиданно, потому что это новое. Мы привыкаем к нему по мере того, как используем его больше. И я не думаю, что это волшебство; спецификация try очень проста.

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

Программисты без опыта работы с Go не поймут chan , defer , 'go , iota , panic , recovery , <- , type assertions, and many other things either without reading the documentation. try` проще простого по сравнению с большинством из них.

  • Не меняет обработку ошибок.

Может быть, это и хорошо, если верить сусликам, просящим оставить if err != nil покое ;-)

@marcopeereboom

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

try имеет ничего общего с обработкой исключений. Вы прочитали полное предложение? Нет ничего похожего на обработку исключений, как, например, в Java или Python. try является явным. Ошибки необходимо указывать в сигнатурах функций. Ошибки необходимо обрабатывать на месте звонка. Разматывания стека нет. И т.п.

@ gale93

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

Я думаю, что у большинства сусликов была такая же мысль, и я много раз читал похожие предложения в системе отслеживания проблем. Но это гораздо большее изменение, чем try . Было бы неразумно делать это только для оператора if . Таким образом, вам придется изменить это везде, где принимается блок. Без { обозначающего начало блока, вы должны указать способ разделения конца условного выражения. Вы должны обновить грамматику, синтаксический анализатор, gofmt и т. Д. Это полностью изменило бы поверхность языка.

@ngrilly
Умеренность и простота языка важны.

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

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

Привет @kroppt!

простой язык важен

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

Я оцениваю решение, основываясь на том, поможет оно или повредит, не обязательно полностью придерживаясь определенной философии.

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

Вы правы, что некоторые вещи в спецификации нарушают определенные принципы, на которых был основан go.

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

@ngrilly
https://talks.golang.org/2012/splash.article описывает некоторые концепции, лежащие в основе того, что отличает go других, - среди прочего, ясность и простоту. Я думаю, что это конфликт, который некоторые из нас видят в этом новом изменении. Это проще, но менее понятно. Мне кажется, что выигрыш в простоте меньше, чем потеря ясности. Может я ошибаюсь и просто осторожничаю.

@kroppt Я читал эту статью десятки раз ;-) Я не согласен с тем, что попытка запутывает код. try - это просто встроенная функция, используемая для разложения повторяющегося кода. Как программисты, мы делаем это постоянно. Когда мы идентифицируем повторяющийся паттерн, мы выделяем его в новую функцию. Если бы мы этого не сделали, у нас была бы одна длинная функция main (), в которую был бы встроен весь наш код.

@ngrilly
То, что вы описываете, есть в моем "профи" разделе:

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

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

Я не согласен с тем, что попытка запутывает код

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

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

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

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

Я также думаю, что, хотя проверки if err != nil могут показаться утомительными, мне не нужно их набирать. Я просто поручаю это делать своему редактору.

@kroppt

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

Но вы можете использовать этот аргумент для любого вызова функции! Если я вызываю strings.HasPrefix("foobar", "foo") , код запутывается? Вы бы предпочли писать и читать l := len("foo"); len("foobar") >= l && s[0:l] == "foo" ?

@rossmcf

Каждая проверка ошибок является явной.

try все еще явно проверяет ошибку. В этом смысл попытки.

Я также думаю, что, хотя проверки err! = Nil могут показаться утомительными, мне не нужно их вводить.

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

try все еще явно проверяет ошибку
Разница между абстракцией try и strings.HasPrefix заключается в том, что try неявно возвращает.
При чтении кода go я знаю, что поток выполнения остается в моей функции до тех пор, пока я:

  • прочитайте закрывающую скобку функции без возвращаемых типов
  • прочтите ключевые слова return , panic
  • прочитать syscall.Exit(code)
    С try я не мог читать код таким же образом. Визуальное сканирование строк и отсутствие операторов возврата больше не будет означать «либо все эти строки выполняются, либо один блок, либо программа завершается».

@ngrilly Вы можете ответить более чем одному человеку в сообщении. К вашему сведению, 10 ответов за пару часов, пять из которых подряд идут в один момент, затрудняют отслеживание обсуждения. Прочитав ваши сообщения, помимо некоторых заблуждений, я не увидел никаких новых конкретных аргументов, описывающих преимущества попытки. Я видел одно преимущество: предотвращение ввода if err != nil . Это происходит за счет внедрения новых способов утечки ресурсов , возможности писать менее сжатый код и, что хуже всего, позволяет вкладывать попытки .

Я считаю, что спецификация и аргументы, выдвинутые сторонниками, вводят в заблуждение, в настоящее время она предоставляет только лучшие примеры, не показывая ни одного из худших примеров. Он не оценивает и не упоминает вышеупомянутые отрицательные недостатки или какие-либо потенциальные смягчающие факторы. Это не оправдывает, почему он не ограничивает реализацию попытки предложенным и продемонстрированным использованием. Инструмент tryhard используется для отображения более компактного кода, который субъективно обеспечивает лучшую эстетику для некоторых людей, без инструмента trytoohard , который показывает все возможности того, что он может делать, например, глубоко вложенная попытка заявления. Наконец, само имя повсеместно связано в мире программирования с исключениями, позволяя ему быть вложенным и связанным с ошибками, и помещать его рядом как встроенное средство для несвязанного восстановления и паники, оставляя вещи просто неуместными. Я верю и верю в способность авторов Go придумать что-то лучшее.

Здесь слишком много затрат, чтобы оправдать их одной дополнительной ценностью, которую я вижу в ответах сторонников: «Мне больше не нужно набирать if err != nil » - то, что горячо защищалось и укоренилось вместе с ошибками, ценности всего сообщества Go. Мы приближаемся к десятилетию кода, написанного с использованием if err != nil - которые в то же время используются с большим успехом в некоторых из наиболее заметных технологических достижений (докеры, k8s, ...).

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

У меня есть мнения о том, какой метод проще, но это мнения. Данная попытка проста, а текущие явные проверки просты. Великолепно, оба пути просты. Проблема для меня в том, что это увеличивает когнитивную нагрузку как читателя, так и автора любого данного кода. Теперь оба должны интерпретировать разные способы действия. И писатель должен выбрать, каким образом делать что-то и / или как действовать иначе, чем остальная часть пакета или проекта, над которым они работают. Если бы попытка заменяла явную проверку, это все равно увеличивало бы когнитивную нагрузку из-за неявных возвратов в качестве еще одного предмета для анализа.

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

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

@cstockton

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

Ян предложил 7 дней назад перенести это обсуждение на golang-nut именно по этой причине (нет возможности ответить на конкретный комментарий, нет цепочки), предложение, которое было отклонено, чтобы убедиться, что обсуждение будет «официальным». У нас есть то, что мы просили.

@therealplato

Разница между абстракцией try и strings.HasPrefix заключается в том, что try неявно возвращает.

Это правда. При чтении функции и поиске точек «выхода» нам придется искать return, panic, log.Panic, os.Exit, log.Fatal и т. Д. И пробовать. Это такая проблема? Количество точек выхода в функции останется прежним и по-прежнему будет явно помечено, с попыткой или без нее.

прочтите ключевые слова return, panic

паника - это не ключевое слово; это встроенная функция. Если мы собираемся критиковать предложение команды Go, которая, вероятно, более компетентна в языковом дизайне, чем любой из нас, то, по крайней мере, мы должны оказать им услугу, правильно определив вещи. 😉

При чтении функции и поиске точек «выхода» нам придется искать return, panic, log.Panic, os.Exit, log.Fatal и т. Д. И пробовать. Это такая проблема?

Это проблема, потому что try может появляться буквально везде, где может появиться выражение. Каждая отдельная точка выхода в Go может _только_ выполняться как один оператор, try - единственное, что может отображаться как выражение.

@ngrilly

Ян предложил 7 дней назад перенести это обсуждение на golang-nut именно по этой причине (нет возможности ответить на конкретный комментарий, нет цепочки), предложение, которое было отклонено, чтобы убедиться, что обсуждение будет «официальным». У нас есть то, что мы просили.

начать сообщение

@ user1
ответ 1

@ user2
ответ 2

конец сообщения

Вот что имелось в виду.

@cstockton

Я видел одно преимущество: предотвращение ввода if err! = Nil.

try предотвращает повторный ввод и чтение if err != nil { return ..., err } (отформатированного в 3 строки), а не только if err != nil .

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

Упомянутый вами риск утечки ресурсов можно предотвратить с помощью vet и lint .

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

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

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

Может быть, это чувство взаимно. Мы все милые суслики; не впадаем в оценочные суждения ;-)

Я вижу отрыгивание в ответах сторонников: "Мне больше не нужно печатать, если err! = Nil"

Опять же, мне больше не нужно набирать l := len("foo"); len("foobar") >= l && s[0:l] == "foo" .
Вместо этого я могу использовать strings.HasPrefix("foobar", "foo") .
Чем так отличается от try ?
Я читал ранее, что вы примете "ограниченный" try , который будет называться check и запретит вложение.

Мы приближаемся к десятилетию кода, написанного с использованием if err! = Nil - что в то же время является одним из наиболее заметных технологических достижений (docker, k8s, ...), которое используется с большим успехом.

У нас также есть много отличного кода, написанного на C, C ++, Java и т. Д. При таких рассуждениях у нас не было бы Go.

Читая обсуждения обработки ошибок в Go, я не чувствовал, что все были на одной странице относительно предложения try , поэтому я решил написать сообщение в блоге, демонстрирующее, как try может использоваться: https://faiface.github.io/post/how-to-use-try/

Связанное обсуждение Reddit: https://www.reddit.com/r/golang/comments/c9eo3g/how_to_use_try_faiface_blog/

Я знаю, что эта проблема против try , но надеюсь, что мой пост может открыть новые перспективы :)

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

Плюсы:

  • Уменьшить шаблон
  • Простой
  • Существующий шаблон среди других языков
  • По желанию

Минусы:

  • Кривая обучения
  • Основная магия
  • Новые виды ошибок
  • Go самоуверен и прагматичен с if != nil но вы можете использовать try

Я чувствую, что это сообщество особенно. здесь отличается от людей, проголосовавших в Go Survey [1] .
Избиратели могут не выбрать это в качестве основной проблемы, а оставить это на будущее.
Но считалось, что это повлияло на его размещение.

IMO, функция добавления языка устарела, и современный способ программирования добавляет больше функций в редакторы, например, Emmet или языковые фрагменты, сворачивание и раскраску кода, рефакторинг и форматирование, отладку и тестирование, предлагая решение ошибки и цитируя Godoc или переполнение стека , Пользовательский интерфейс поверх исходного кода и оставьте исходный код подробным
код свернуть if err != nil в try

@ngrilly

try предотвращает повторный ввод и чтение if err! = nil {return ..., err} (форматируется в 3 строки), а не только if err! = nil.

Вы верите, что я сказал, что «он не позволяет вам печатать, если err! = Nil» означает, что я совершенно забыл, что мы также читаем код, который мы набираем?

Упомянутый вами риск утечки ресурсов можно предотвратить с помощью ветеринара и пуха.

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

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

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

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

Интересно, давайте разберемся с этим:

1) Риск чрезмерной вложенности вызовов функций не является конкретным.

Да, здесь все понимают, как работают функции.

2) Любые вызовы функций могут быть слишком вложенными.

Да, здесь все понимают, как работают функции.

3) Код ревью и линтинг, как всегда, помогут.

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

Может быть, это чувство взаимно. Мы все милые суслики; не впадаем в оценочные суждения ;-)

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

Опять же, мне больше не нужно вводить l: = len ("foo"); len ("foobar")> = l && s [0: l] == "foo".
Вместо этого я могу использовать strings.HasPrefix ("foobar", "foo").
Чем отличается попытка?

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

Чем ( strings.HasPrefix ) так отличается от try ?

strings.HasPrefix

func HasPrefix

func HasPrefix (s, строка префикса) bool

HasPrefix проверяет, начинается ли строка s с префикса.

пытаться

func try - это новая встроенная функция, похожая на функцию, называемую try с подписью (псевдокод)

func try(expr) (T1, T2, … Tn)

где expr обозначает выражение входящего аргумента (обычно вызов функции), дающее n + 1 результирующее значение типов T1, T2, ... Tn и ошибку для последнего значения. Если expr оценивается как одно значение (n равно 0), это значение должно относиться к типу error, а try не возвращает результат. Вызов try с выражением, которое не дает последнего значения типа error, приводит к ошибке времени компиляции.

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

Вызов try с вызовом функции f () как в (псевдокод)

x1, x2, … xn = try(f())

turns into the following (in-lined) code:

t1, … tn, te := f()  // t1, … tn, te are local (invisible) temporaries
if te != nil {
        err = te     // assign te to the error result parameter
        return       // return from enclosing function
}
x1, … xn = t1, … tn  // assignment only if there was no error

Другими словами, если последнее значение, созданное «expr» типа error, равно nil, try просто возвращает первые n значений с удаленной последней ошибкой nil. Если последнее значение, созданное expr, не равно нулю, то для переменной результата ошибки включающей функции (называемой err в приведенном выше псевдокоде, но она может иметь любое другое имя или быть безымянным) устанавливается значение ошибки, отличное от нуля, и включающая функция возвращается. Если включающая функция объявляет другие именованные параметры результата, эти параметры результата сохраняют то значение, которое они имеют. Если функция объявляет другие безымянные параметры результата, они принимают соответствующие им нулевые значения (что равносильно сохранению значения, которое у них уже есть).

Если попытка используется в множественном присваивании, как на этой иллюстрации, и обнаружена ошибка, отличная от nil, присвоение (определяемым пользователем переменным) не выполняется, и ни одна из переменных в левой части назначение изменено. То есть try ведет себя как вызов функции: его результаты доступны только в том случае, если try возвращается на фактический сайт вызова (в отличие от возврата из включающей функции). Как следствие, если переменные в левой части являются именованными параметрами результата, использование try приведет к другому результату, чем типичный код, найденный сегодня. Например, если a, b и err являются именованными параметрами результата включающей функции, этот код

a, b, err = f()
if err != nil {
        return
}

всегда будет устанавливать a, b и err, независимо от того, вернула ли f () ошибку или нет. Наоборот

a, b = try(f())

оставит a и b без изменений в случае ошибки. Хотя это небольшая разница, мы считаем, что подобные случаи редки. Если ожидается текущее поведение, оставьте оператор if.

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

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

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

_Или_ предложение должно содержать примеры, соответствующие реализации, чтобы все люди, рассматривающие его, могли увидеть то, что неизбежно появится в коде Go. Наряду со всеми острыми случаями, с которыми мы можем столкнуться при устранении неполадок не идеально написанного программного обеспечения, которое происходит на любом языке / в любой среде. Он должен отвечать на вопросы, например, как будут выглядеть трассировки стека с несколькими уровнями вложенности, легко ли распознать места ошибок? А как насчет значений методов, анонимных функциональных литералов? Какой тип трассировки стека производит приведенное ниже, если строка, содержащая вызовы fn (), терпит неудачу?

fn := func(n int) (int, error) { ... }
return try(func() (int, error) { 
    mu.Lock()
    defer mu.Unlock()
    return try(try(fn(111111)) + try(fn(101010)) + try(func() (int, error) {
       // yea...
    })(2))
}(try(fn(1)))

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

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

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

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

Плюсы:

  • Уменьшить шаблон
  • Простой
  • Существующий шаблон среди других языков
  • По желанию

Минусы:

  • Кривая обучения
  • Основная магия
  • Новые виды ошибок
  • Go самоуверен и прагматичен с if != nil но вы можете использовать try

Я чувствую, что это сообщество особенно. здесь отличается от людей, проголосовавших в Go Survey [1] .
Избиратели могут не выбрать это в качестве основной проблемы, а оставить это на будущее.
Но считалось, что это повлияло на его размещение.

IMO, функция добавления языка устарела, и современный способ программирования добавляет больше функций в редакторы, например, Emmet или языковые фрагменты, сворачивание и раскраску кода, рефакторинг и форматирование, отладку и тестирование, предлагая решение ошибки и цитируя Godoc или переполнение стека , Пользовательский интерфейс поверх исходного кода и оставьте исходный код подробным
код свернуть if err != nil в try

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

Чтобы удаленно переделать что-нибудь вроде типа Result Rust, нам понадобится гораздо больше, чем дженерики. Даже если _ если_ тип Result может быть создан исключительно с помощью дженериков, начинающим программистам нужно будет знать дженерики, прежде чем они смогут правильно обработать ошибку или вернуть ошибку из функции "способом Result "

@deanveloper , я

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

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

можно ли это решить с помощью сниппета или макроса клавиатуры? тогда это не проблема.

@txgruppi

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

70% всего кода обработки ошибок в стандартной библиотеке в настоящее время подходит для try как выяснил Роберт Гриземер с помощью своего инструмента tryhard . Другие будут иметь право с изменениями кода, такими как использование (еще не существующей) функции fmt.HandleErrorf . Надеюсь, вы не хотите называть стандартную библиотеку плохим кодом.

можно ли это решить с помощью сниппета или макроса клавиатуры? тогда это не проблема.

Речь также идет о чтении кода. Вот почему нам не нравятся thing.Thing thing = new thing.Thing(thing.THING);

@faiface , "if err! = nil"
Не мешает ли отсутствие Generics разрабатывать качественное программное обеспечение? Да, это так.

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

@faiface стандартная библиотека не очень хорошо отображает реальный код Go. Это связано с тем, что для стандартной библиотеки гораздо более вероятно, что стандартная библиотека просто пропустит ошибки без добавления контекста, например, io/ioutil никогда не нужно украшать ошибки, она может просто пропустить ошибку, которая произошла в io . Роберт Гриземер также признал, что stdlib не совсем лучший представитель реального кода Go, однако сейчас я использую мобильный телефон и не хочу искать комментарий. Я почти уверен, что это было относительно близко к его первоначальному посту в стиле tryhard.

@deanveloper @faiface При запуске против Go Corpus :

--- stats ---
 401679 (100.0% of  401679) func declarations
  97496 ( 24.3% of  401679) func declarations returning an error
 991348 (100.0% of  991348) statements
 217490 ( 21.9% of  991348) if statements
  88891 ( 40.9% of  217490) if <err> != nil statements
    485 (  0.5% of   88891) <err> name is different from "err" (-l flag lists details)
  59500 ( 66.9% of   88891) return ..., <err> blocks in if <err> != nil statements
  29391 ( 33.1% of   88891) complex error handler in if <err> != nil statements; cannot use try (-l flag lists details)
    596 (  0.7% of   88891) non-empty else blocks in if <err> != nil statements; cannot use try (-l flag lists details)
  52810 ( 59.4% of   88891) try candidates (-l flag lists details)

Таким образом, в реальном коде 40% операторов if написаны для проверки ошибок, а try может устранить 59% из них прямо из коробки.

Я согласен. Меня устраивает if err! = Nil. Это просто и понятно для функций, возвращающих значения одиночной ошибки. Мне также нравится пакет ошибок и его функции причины / переноса, когда имеет значение контекст ошибки. Использование пользовательских ошибок со свойством кода (насколько мне известно) требует, чтобы вы либо подтвердили тип, либо вообще использовали что-то вместо стандартного интерфейса ошибок. В любом случае, я никогда не обнаруживал, что читаю или пишу код Go и чувствую какое-то раздражение по поводу того, как в настоящее время работает обработка ошибок. Я столкнулся с неприятностями, когда в результате обработки коллекции элементов может возникнуть несколько ошибок. Однако это проблема дизайна, а не языка.

Обратите внимание: когда я говорю «когда контекст ошибки имеет значение», я имею в виду ситуацию, когда, возможно, произошла сетевая сбой, и поэтому я хочу повторить попытку, или, возможно, результаты вызова типа «найти» не дали результатов, потому что были на самом деле ни одного, или мой дергающийся палец добавил случайную букву «s» к моему SQL-запросу, из-за чего он взорвался (со мной такое случается часто ... Мне, вероятно, следует проверить на повреждение нервов).

7/5/19 Николас Грилли на [email protected] написал:

@kroppt Я читал десятки раз ;-) Я не согласен с тем, что попытка - это
запутывание кода. try - это просто встроенная функция, используемая для учета некоторых
повторяющийся код. Как программисты, мы делаем это постоянно. Когда мы идентифицируем
повторяющийся шаблон, мы выделяем его в новую функцию. Если бы мы этого не сделали, мы
будет одна длинная функция main (), в которую встроен весь наш код.

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

При этом: «Когда мы выявляем повторяющийся паттерн, мы учитываем его
новая функция ". Конечно, но не путем предоставления конфликтующей конструкции
что на этой поздней стадии развития Go имеет два
скрытые функции: первая касается функций, которые "возвращают
список аргументов заканчивается на error value как специальное (или все остальное
как семантическая ошибка), а во-вторых, он обеспечивает скрытый поток управления
объезд, который аналогичен, но не полностью идентичен «возвращению»
утверждение.

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

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

Более глубоко, что ни один язык, за исключением, пожалуй, СНОБОЛА,
который мне знаком, совершил прыжок, который Роб Пайк описал как
«ошибки - это ценности» в той степени, в какой есть Go, но что-то потеряно
в процессе: ошибка «условие» - это «не» значение. Успешный
завершение функции - это особый случай, и каждый из возможных
отказ.

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

Чтобы проиллюстрировать эту мысль, рассмотрим возвращаемые значения от Reader:
io.EOF - это особый случай, который иногда бывает успешным, а иногда
сбой, но по стандартам Go это явно ошибка ("io.Err! = nil").
Мы собираемся найти способ сократить и это? Почти наверняка
нет, потому что мы привыкли «прощать» его «неправоту».

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

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

И сравнение с тернарным оператором одинаково верно: if?:
не допускается "во избежание злоупотребления", то попытки не должны быть разрешены,
либо, по крайней мере, на этих основаниях.

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

У нас паника / выздоровление и его нужно раскручивать с третьесортного
гражданин желающему помощнику может быть, со всеми объяснениями
которые делают новичков (и я, признаюсь, боюсь этого) больше
уверенно его использую.

Конструкция defer в обработке ошибок (которую я принял -
не осознавая его импорта где-либо еще - последовательно в доработке
Транзакции SQL: tx.Rollback / tx.Commit) были для меня откровением.
Может быть больше принципов, которые можно изучить "в" рамках
что уже предлагает Go: давайте пока останемся в рамках этой рамки.

Одна из таких вещей, вне манжеты, - это перейти к сообщению об ошибках.
функция список "error.Methods", которые должны выполняться в соответствии с обычными
условия (io.EOF, sql.ErrNoRows) вместо сообщения результата
как черно-белые. Но я в таких вопросах необразован, мои предложения
слишком наивны, пусть другие (Роджер, ты слушаешь?)
идеи к реализации.

Лючио.

"Это не утомительно печатать. Утомительно читать, когда у нас одинаковые
Везде 3 строчки. Это повторение, и мы, программисты, обычно
стараются их исключить. Может у попробовать есть и другие недостатки, но не этот
один я думаю ".

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

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

Но «err! = Nil» - очень знакомый маркер, а при «чтении» - как
в отличие от "поиска" с помощью редактора, этот шаблон легко
место и легко отменить. Факторинг - это не то же самое
лига вообще. И цена неправильная.

«Мы, программисты», склонны в первую очередь выделять более сложные шаблоны,
даже если они случаются редко. Мы также знаем, что "if err! = Nil {return
err} "компилируется в очень простую последовательность инструкций, если
никто не делает, пусть поднимет здесь руку. Можно ли быть одинаково
уверены, что это произойдет с «попробуй - функция»?

Лючио.

@lootch Эй, чувак, называть людей

Мы как программисты исключаем повторяющиеся шаблоны, это абсолютно верно.

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

Ваши контраргументы в основном таковы: «Да ладно, это не так уж важно». Что ж, для многих это так.

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

Это супер высокомерное охранение ворот. Кроме того, инструмент tryhard показал, что try напрямую применимо во многих сегодняшних кодовых базах Go. Его можно напрямую применить к 70% кода обработки ошибок в стандартной библиотеке. Я полагаю, что с изменениями кода (для использования оформления ошибок с помощью defer и т. Д.) Это будет применимо к более чем 80% обработки ошибок во всем коде Go.

Согласен, здесь я перешагнул планку. Я приношу извинения.

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

7/7/19 Михал Штрба [email protected] написал:

@lootch Эй, чувак, называть людей
почти уверен, что это не так, поскольку утверждения, которые вы отметили как таковые, довольно хороши
разумный.

Мы как программисты исключаем повторяющиеся шаблоны, это абсолютно
правда.

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

Ваши контраргументы в основном таковы: "Да ладно, это не такая уж
большое дело ». Что ж, для многих это так.

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

Это супер высокомерное охранение ворот. Кроме того, инструмент tryhard показал, что
try напрямую применимо во многих сегодняшних кодовых базах Go. Это может быть
непосредственно применяется к 70% кода обработки ошибок в стандартной библиотеке. С участием
изменения в коде (чтобы использовать оформление ошибок с помощью defer и т. д.), я считаю
он будет применим к более чем 80% обработки ошибок во всем коде Go.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую или просмотрите его на GitHub:
https://github.com/golang/go/issues/32825#issuecomment -508971768

-
Лусио Де Ре
2 Piet Retief St
Кестелл (Восточный свободный штат)
9860 Южная Африка

Тел .: +27 58 653 1433
Сотовый: +27 83 251 5824
ФАКС: +27 58 653 1435

@lootch Поддержи свою застенчивость! Я могу понять разочарование, наблюдая за обсуждением «Идите по кругу».

Я тоже это вижу, но я на другой стороне.

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

7/7/19 Михал Штрба [email protected] написал:

[...]
Возможно, обе стороны просто не понимают другую сторону. Есть ли у тебя
прочтите мое сообщение в блоге под названием " Как использовать" 'пытаться'? где я пытаюсь
показать, как на практике будет выглядеть использование слова "попробуйте", стараюсь изо всех сил остаться
беспристрастный?

Признаюсь, я этого не делал, я горячо желаю, чтобы никогда не приходилось :-)

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

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

Лючио.

@lootch Я приложил все усилия, чтобы показать как можно больше аспектов «попытки» (т.е. как когда это применимо, так и когда нет) в небольшом количестве кода и сделать его максимально непредвзятым и реалистичным. Но, конечно, не верьте мне на слово :)

Это был самый высокий комментарий к соответствующему обсуждению Reddit :

Это полезный беспристрастный гипотетический пример. Спасибо за то, что добавили в разговор что-то конструктивное, а не просто "отстой".

@lootch Я приложил все усилия, чтобы показать как можно больше аспектов «попытки» (т.е. как когда это применимо, так и когда нет) в небольшом количестве кода и сделать его максимально непредвзятым и реалистичным. Но, конечно, не верьте мне на слово :)

Это был самый высокий комментарий к соответствующему обсуждению Reddit :

Это полезный беспристрастный гипотетический пример. Спасибо за то, что добавили в разговор что-то конструктивное, а не просто "отстой".

Функция с путем к файлу в качестве аргумента? Одно только это могло бы стать причиной того, что этот код не прошел мой обзор. Что делать, если некоторые поля отсутствуют? Заказано повторно?

@sirkon Конечно, чтобы не быть слишком длинным, пример упрощен. Однако изменения, которые потребуются для исправления возникших проблем, не влияют на методы обработки ошибок, и это все, что здесь имеет значение.

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

Потому что ты так сказал?

  1. Начните с заголовка вашего блога: он должен называться «как не писать», потому что, я повторяю, использование пути к файлу в качестве параметра - действительно плохая практика, и, честно говоря, весь приведенный ниже код тоже.
  2. Вы понимаете это
    go resp := Respondent{ Name: name, Gender: try(parseField(s, &line, "gender")), OS: try(parseField(s, &line, "os")), Lang: try(parseField(s, &line, "lang")), }
    будет выдавать плохие сообщения об ошибках? Должно быть как минимум сообщение об ошибке неожиданного поля и сообщение об ошибке отсутствия поля. Диагностика вашего сценария некачественная.

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

@sirkon Давай, не устраивай флейм.

Вы понимаете, что это приведет к плохим сообщениям об ошибках?

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

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

PS Посмотрел ваши репозитории. Вы понимаете, что Go - плохой инструмент для ваших задач?

Вы предлагаете другое для моих задач? Я использовал довольно много. Кстати, это совсем не по теме.

@faiface

Вы предлагаете другое для моих задач? Я использовал довольно много. Кстати, это совсем не по теме.

Ржавчина? C ++?

@sirkon

Ржавчина? C ++?

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

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

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

@deanveloper Спасибо за комментарий!

Кстати

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

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

parse respondnts.txt: open respondnts.txt: no such file or directory
parse respondents.txt: line 12: parse field gender: expected "gender:"
parse respondents.txt: line 9: expected empty line
parse respondents.txt: line 4: parse field lang: EOF

@faiface Моя ошибка, я должен был быть более конкретным. try препятствует оформлению ошибок, когда вы хотите, чтобы в одной функции было несколько сообщений об ошибках. Это можно было сделать с помощью check/handle draft и встречных предложений "именованного обработчика". Было бы очень полезно в указанном конкретном случае (где вы использовали try при инициализации структуры), чтобы иметь возможность добавлять украшения вокруг каждого сообщения, но, к сожалению, предложение попробовать сделать это немного сложно. без написания собственной функции.

Однако проверка / дескриптор не помогли бы так сильно в вашем конкретном случае. Но предлагаемая идея catch и другие контрпредложения к try могли бы справиться с ошибками, добавив дополнительное украшение.

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

..., err := functionThatCanFail(...)
try(errors.Wrapf(err, ...))

Или просто разделите большую функцию на несколько маленьких.

@faiface, на мой взгляд, в этот момент следует просто использовать if err != nil , но я думаю, это вопрос предпочтений.

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

На самом деле я не супер против try , но я тоже не очень сторонник. Я думаю, что есть другое решение получше.

@deanveloper

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

Верно, но также не нужно украшать их по-другому, потому что все необходимое конкретное украшение происходит от parseField .

Я думаю, что есть другое решение получше.

Вполне возможно! Если я увижу лучшее решение, я скину try через минуту :)

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

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

Представьте себе функцию, которая добавляет два блока данных в один файл. Как бы вы отличили, какое из этих добавлений завершилось ошибкой, если вы едва вернете инструкцию «не удалось записать в файл»?

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

@mklimuk Ключевая часть моего комментария - «большую часть времени». Приведенный вами пример, вероятно, лучше всего подходит для if err != nil . Как неоднократно отмечалось, try не предназначен для обработки всех ситуаций, а только наиболее распространенных.

И данные показывают, что это действительно так, поскольку 70% всего кода обработки ошибок в стандартной библиотеке могут использовать try из коробки", и то же самое касается 59% всего кода обработки ошибок в библиотеке. дикий.

@faiface: То , что try может заменить явную обработку ошибок, не означает, что это должно быть. В моем случае возврат ошибки без добавления к ней контекста - не самая распространенная ситуация. Все наоборот :)

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

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

func doSomething() (err error) {
    defer fmt.HandleErrorf(&err, "doing something")

    x := try(oneThing())
    try(anotherThing(x))
    // ...
}

Следует понимать, что в большинстве случаев oneThing() и anotherThing() сами по себе возвращают достаточный контекст, поэтому вполне достаточно обернуть его простым "doing something: ..." .

В качестве примечания, я думаю, мы могли бы использовать некоторые соглашения о том, кто выполняет оформление. В stdlib некоторые функции делают это, например copy: x to y или что-то подобное, я лично оставлял оформление на усмотрение вызывающей стороны, поскольку у нее есть аргументы.

Например, если бы у меня был Copy() я бы сделал что-то вроде return errors.Wrap(err, "writing") а вызывающий абонент, использующий Copy() , обернулся бы errors.Wrapf(err, "copying from %v to %v", src, dst) или аналогичным.

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

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

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

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

@lpar Не могли бы вы привести конкретный пример этого?

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

@lpar Хорошо, это еще один пример, в котором if err != nil останется в использовании. Или вы разбиваете свою логику на несколько функций.

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

Screenshot 2019-07-07 at 6 30 42 PM

@ abejide001 предложение try не является традиционным "попробуй / поймай" из многих других языков, оно больше похоже на макрос try! в Rust. Хороший мем хоть лол

Упс - отправлено не в тот выпуск. Перемещено на https://github.com/golang/go/issues/32437#issuecomment -509024693.

Недавно я опубликовал предложение # 32968, основанное на моем несогласии с опасной способностью к вложению, которой обладает макрос try . Хотя я надеюсь, что в нем нет серьезных недостатков, я как автор не тот человек, который их видит. Поэтому я хотел бы попросить свой лагерь _do not try_ (вы :) увидеть, оценить и прокомментировать его.


Отрывок:

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

Ограничения дизайна (соблюдены)

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

пример использования

// built-in 'check' macro signature: 
func check(Condition bool) {}

check(err != nil) // explicit catch: label.
{
    ucred, err := getUserCredentials(user)
    remote, err := connectToApi(remoteUri)
    err, session, usertoken := remote.Auth(user, ucred)
    udata, err := session.getCalendar(usertoken)

  catch:               // sad path
    ucred.Clear()      // cleanup passwords
    remote.Close()     // do not leak sockets
    return nil, 0, err // dress before leaving
}
// happy path

// implicit catch: label is above last statement
check(x < 4) 
  {
    x, y = transformA(x, z)
    y, z = transformB(x, y)
    x, y = transformC(y, z)
    break // if x was < 4 after any of above
  }

Надеюсь, это поможет, наслаждайтесь!

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

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

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

Полностью согласен, оставьте "if err! = Nil" в покое.

@ abejide001 предложение try не является традиционным "попробуй / поймай" из многих других языков, оно больше похоже на макрос try! в Rust. Хороший мем хоть лол

Одно это меня беспокоит, Go уже стал странным языком для новичков, и теперь мы должны объяснить, почему в try есть индивидуальная логика. FWIW, я не думаю, что фраза «Это сделал Rust» - это веская причина для оправдания добавления чего-либо в язык - это просто малоизвестно.

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

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

Кажется, есть некоторая путаница в том, чего именно пытается достичь. ИМХО, проблема не в написании нескольких блоков if, которые проверяют наличие ошибок. Вы пишете их один раз, и все готово. Проблема заключается в чтении кода, в котором несколько таких блоков. Мы гораздо больше читаем, чем пишем. И эти блоки затемняют реальный код, потому что переплетаются с ним. Что еще хуже, в большинстве случаев они почти одинаковы, только с незначительной разницей в строках где-то в этом блоке if.

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

@icholy написал:

Огромное количество откликов сообщества требовало более рациональной обработки ошибок (из ежегодного опроса). Команда Go сейчас занимается этой проблемой.

Я только что нашел здесь опрос: https://blog.golang.org/survey2018-results

Очевидно, возник вопрос: «Какая самая большая проблема, с которой вы лично сталкиваетесь при использовании Go сегодня?» с возможным ответом «Обработка ошибок».

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

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

Я только что рассмотрел предложение «попробовать», но, если я не упускаю чего-то, он не говорит, почему это предлагается, кроме «исключить шаблонные операторы if [...}». Но нет никакого упоминания о том, почему необходимо исключить эти стандартные операторы if.

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

Здесь люди возражают против try потому что считают, что все возвращаемые ошибки должны быть аннотированы. Реальность такова, что в текущем корпусе кода (включая стандартную библиотеку) высокий процент проверок ошибок имеет ~ голые ~ неотмеченные сообщения об ошибках, и для них можно использовать try . Ваша вера в то, как ДОЛЖЕН быть код, не имеет ничего общего с тем, как ЕСТЬ код. Избавь меня от своей догмы.

@icholy Игнорирование ошибок означает, что разработчик не заботится об этой ошибке. Ошибка незначительна или вызывающая считает невозможной. Если это так, то «попытка» так же бессмысленна, вызывающий просто не будет заключать функцию в «попытку».

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

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

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

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

Если люди хотят принять участие, вот ссылка, сокращенная для публикации:

https://forms.gle/gaCBgxKRE4RMCz7c7

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

@ lane-c-wagner, вы пытаетесь сказать, что возвращение неаннотированной ошибки - это то же самое, что ее не возвращать вообще? edit: исправлен предыдущий комментарий

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

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

Пожалуйста, перестаньте говорить «что все могут игнорировать» try . Мы читаем код, написанный другими.

@ tv42 Не знаю, обращаетесь ли вы ко мне сюда , но я тоже это сказал, и вы правы . Виновен по обвинению. Я постараюсь быть более осторожным с подобными обобщениями. Спасибо.

@griesemer, которого вы

И мне все еще нужны типы сумм.

Это предложение о том, как gofmt в настоящее время форматирует, если err! = Nil

(Это не мнение о предложении try ().)

Когда оператор if возвращает однострочное ненулевое значение ошибки, например:

err := myFunc()
if err != nil {
    return err
}

gofmt может ослабить собственное правило оператора if и отформатировать его в одну строку следующим образом:

err := myFunc()
if err != nil { return err }

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

Должно быть какое-то суждение о том, где провести черту (признанный каламбур) с этим
изменение правила gofmt. Он может включать некоторые украшения, например:

err := myFunc()
if err != nil { return fmt.Errorf("myFunc() blew up! %v", err }

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

Предложение _try_ было отозвано: https://github.com/golang/go/issues/32437#issuecomment -512035919

Дженерики кто-нибудь?

Это предложение о том, как gofmt в настоящее время форматирует, если err! = Nil

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

ИМО, проблема здесь скорее не в том, как выполняется обработка ошибок, а в том, игнорируется ли она. Разве нельзя было бы оставить синтаксис if err != nil как есть, но ограничить игнорирование возвращаемых значений Error ? Например, сделайте это предупреждением / ошибкой компилятора с опцией снижения серьезности для устаревшего кода.

ИМО, проблема здесь скорее не в том, как выполняется обработка ошибок, а в том, игнорируется ли она. Разве нельзя было бы оставить синтаксис if err != nil как есть, но ограничить игнорирование возвращаемых значений Error ? Например, сделайте это предупреждением / ошибкой компилятора с опцией снижения серьезности для устаревшего кода.

Многим нужен линтер, показывающий игнорируемые ошибки.

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

я считаю, что https://github.com/kisielk/errcheck ценен для сообщения мне о необработанных ошибках @plyhun @sorenvonsarvort

Как видно из обсуждения № 32437, на данный момент это предложение фактически принято. Закрытие. Если проблема возникнет снова, можно открыть новое предложение.

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

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

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

Спасибо!

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

if err := foo(); err != nil {
if _, err := bar(); err != nil {



md5-6a135eb952fe7b24b3389cb16d3244a1



a, err := bar()
if err != nil {



md5-d52f811d3e31bb368bd8045cfb2e93b4



var err error
baz.A, err = bar()
if err != nil {

Переменная err не должна существовать в области действия функции после завершения блока if err != nil {} . Вот мое предложение, которое основано на предложении try() по устранению проблемы https://github.com/golang/go/issues/33161. Я хотел бы получить конструктивный отзыв.

Переменная err не должна существовать в области действия функции после завершения блока if err! = Nil {}.

почему "не должно" его существовать после завершения блока if? Компилятор может оптимизировать для этого (если сочтет это необходимым), и при завершении блока err: = stmt () \ nif err! = Nil {} не возникает никакой умственной нагрузки, потому что они почти всегда идут вместе.

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

Ошибки @Freeaqingme не должны существовать после завершения блока if err != nil , в основном потому, что мы уже действуем так, как будто это не так.

В примере CopyFile есть r, err := os.Open(src) за которым следует w, err := os.Create(dst) . Второй err затеняет первый. Затенение переменных обычно не одобряется.

Есть и другие странности. Если у меня есть err := foo() а позже что-то вроде bar.V, err = baz() , если код подвергся рефакторингу и мне больше не нужен foo (), мне нужно будет добавить var err error перед baz line. . Я не думаю, что рефакторинг другого места в функции должен повлиять на другие подобные места.

Технически в

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

второй экземпляр err не затеняет первый экземпляр. На самом деле это одна и та же переменная. См. Обсуждение повторного объявления переменных на https://golang.org/ref/spec#Short_variable_declarations.

func doSomeThing () {
r, err: = os.Open (имя файла)
panic (fmt.Errorf (err, "не удалось открыть файл:% s", имя файла)) //
Здесь паника.

}

В четверг, 10 октября 2019 г., в 11:24, clearcode [email protected] написал:

Думаю, мы можем добавить встроенную функцию:

утверждать()

пример:

func doSomeThing () error {

r, err := os.Open(filename)
assert(err, "failed to open file: %s", filename) // in this step, just return the error

соответственно, err: = http.Get (someURL)
assert (err, «запрос не выполнен»)

}

и еще одна функция, не возвращающая ошибку:

func doSomeThing () {
r, err: = os.Open (имя файла)
assert (err, "не удалось открыть файл:% s", filename) // Здесь паника.

}

поэтому assert (error, args ... interface {}) лучше, чем: if err! = nil; {
return err}

-
Вы получили это, потому что прокомментировали.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/golang/go/issues/32825?email_source=notifications&email_token=AGUV7XQ5HO7GL3YP72R7BV3QN2N55A5CNFSM4H4DL33KYY3PNVWWK3TUL52H4DFMVREXW2HWWWK2TUL52HS4DFMVRXW2HW2HWW2HWW2HW08CMWW2HW2
или отказаться от подписки
https://github.com/notifications/unsubscribe-auth/AGUV7XS4JMK44QHIIR3RSGTQN2N55ANCNFSM4H4DL33A
.

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

Пт, 11 октября 2019 г., в 9:55 Aaaa Einai [email protected] написал:

func doSomeThing () {
r, err: = os.Open (имя файла)
panic (fmt.Errorf (err, "не удалось открыть файл:% s", filename)) // Здесь паника.

}

В четверг, 10 октября 2019 г., в 11:24 clearcode [email protected]
написал:

Думаю, мы можем добавить встроенную функцию:

утверждать()

пример:

func doSomeThing () error {

r, err := os.Open(filename)
assert(err, "failed to open file: %s", filename) // in this step, just return the error

соответственно, err: = http.Get (someURL)
assert (err, «запрос не выполнен»)

}

и еще одна функция, не возвращающая ошибку:

func doSomeThing () {
r, err: = os.Open (имя файла)
assert (err, "не удалось открыть файл:% s", filename) // Здесь паника.

}

поэтому assert (error, args ... interface {}) лучше, чем: if err! = nil; {
return err}

-
Вы получили это, потому что прокомментировали.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/golang/go/issues/32825?email_source=notifications&email_token=AGUV7XQ5HO7GL3YP72R7BV3QN2N55A5CNFSM4H4DL33KYY3PNVWWK3TUL52H4DFMVREXW2HWWWK2TUL52HS4DFMVRXW2HW2HWW2HWW2HW08CMWW2HW2
или отказаться от подписки
https://github.com/notifications/unsubscribe-auth/AGUV7XS4JMK44QHIIR3RSGTQN2N55ANCNFSM4H4DL33A
.

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

type Result<T> interface {
  Expect(err error) T
  OrElse(defaultValue T) T
}

func From<T>(value T, err error) Result<T> { ... }

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

Это так похоже на Rust Ok и Err.
Я думаю, что if err != nil {} может быть лучше немного.

@Yanwenjiepy , это Result Rust.

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

someValue, err := someFunction();
if err != nil {
  panic(err)
}

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

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

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

try {
  foo, throw err := someFunction();
  bar, throw err := foo.get();
  baz, throw err := bar.make();
  qux, throw err := baz.transform();
} catch(err) {
  // "Unable to foo bar baz qux."
  tryHarder();
}

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

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

Спасибо за ссылку. errWriter - вполне приемлемое решение.

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

try {
  foo, throw err := someFunction();
  bar, throw err := foo.get();
  baz, throw err := bar.make();
  qux, throw err := baz.transform();
} catch(err) {
  // "Unable to foo bar baz qux."
  tryHarder();
}

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

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

try {
  foo, throw err := someFunction();  // err could be TypeA and TypeB
  bar, throw err := foo.get();       // err could be TypeB and TypeC
  baz, throw err := bar.make();      // err could be TypeA and TypeC
  qux, throw err := baz.transform(); // err could be TypeB and TypeD
} catch(err) {
  tryHarder(); // tell me how to handle each error?
}

Кому-то понадобится всего 1 минута, чтобы понять приведенный ниже код:

foo, err := someFunction();  // err could be TypeA and TypeB
if err != nil {
 // handle err
}

bar, err := foo.get();       // err could be TypeB and TypeC
if err != nil {
  // handle err
}

baz, err := bar.make();      // err could be TypeA and TypeC
if err != nil {
  // handle err
}

qux, err := baz.transform(); // err could be TypeB and TypeD
if err != nil {
  // handle err
}

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

В этом примере вы совершенно правы.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги