Go: propuesta: ¿dejar "if err! = nil" solo?

Creado en 28 jun. 2019  ·  314Comentarios  ·  Fuente: golang/go

La propuesta de Go2 # 32437 agrega una nueva sintaxis al lenguaje para hacer que el texto estándar if err != nil { return ... } menos engorroso.

Hay varias propuestas alternativas: # 32804 y # 32811 ya que la original no es amada universalmente.

Para lanzar otra alternativa a la mezcla: ¿Por qué no mantenerlo como está ?

Me ha gustado la naturaleza explícita de la construcción if err != nil y, como tal, no entiendo por qué necesitamos una nueva sintaxis para esto. ¿Es realmente tan malo?

FrozenDueToAge Proposal Proposal-Hold error-handling

Comentario más útil

debería haber una sola forma de hacer las cosas

Todos 314 comentarios

Secundo esto. Realmente me gusta cómo decorar cada error antes de devolverlo agrega documentación legible por humanos a la fuente (generalmente formateamos nuestros errores como "no podría [lo que estoy haciendo en estas líneas de código]: [error anterior]") y también a los usuarios errores de lectura.

Los errores generados de esta manera son extremadamente informativos y mucho más fáciles de leer que los seguimientos de pila. Los errores impresos que incluyen seguimientos de pila generalmente asumen que tiene acceso inmediato a las fuentes (es posible que los administradores no tengan ese acceso) y que realmente conoce su camino en el código.

Los errores sin ningún tipo de contexto o seguimiento (la cadena simple "EOF") son absolutamente inútiles. Creo que tener atajos que faciliten la devolución de errores desnudos hará que los programas Go impriman muchos errores inútiles.

En todo caso, deberíamos impulsar y apoyar los errores de decoración con contexto, tal vez con nuevas reglas veterinarias y de pelusa.

También me gusta la verificación explícita de errores. try es confuso y el retorno implícito es extraño.

Creo que, en lugar de repensar los errores, podríamos probar un enfoque alternativo para acortar estos controles.

Aquí hay un ejemplo con el que no estoy necesariamente de acuerdo:

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

Esto permitiría un enfoque más breve pero aún explícito. ¡Y permitiría agregar contexto!

Dicho esto, los ifs en línea son una cosa de Ruby y no se sienten muy Goish, pero esto es solo una lluvia de ideas. Quizás encontremos algo más.


EDITAR: Agregué una propuesta para esto aquí: https://github.com/golang/go/issues/32860

debería haber una sola forma de hacer las cosas

[...] ¿Por qué no dejarlo como está?

Creo que es justo decir que todos conocemos la respuesta a esto. Solo necesita leer una de las diversas propuestas para encontrar la respuesta si sinceramente no sabe.

En mi opinión, hay muy pocos detalles aquí para que podamos tener una discusión enfocada (es decir, no creo que califique como una propuesta) y pronto se convertirá en otro cobertizo para bicicletas lleno de movimientos e ideas que hacen que el código sea menos legible .

Tanto esto.

Podría decirse que entré en Go debido a este manejo explícito de errores. Se encuentra en algún lugar entre el try-catch implícito que muchos lenguajes prefieren y los tipos de funciones como Option o Maybe, que favorecen que se devuelva al usuario y se maneje explícitamente.

No estoy seguro de si una nueva construcción realmente resolvería esto. Si envolvió if err := nil en una función auxiliar como esta, podría ayudar un poco (perdón por mi oxidado Go):

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

Pero el problema que hace que esta función auxiliar sea menos útil en general es el sistema de tipos, que es un tema diferente.

Secundo esto. if err != nil { return err } no es parte de ningún código en nuestra base de código. Por lo tanto, la "macro" de prueba no tiene ningún sentido. Solo devolvemos errores envueltos, con un mensaje que describe el contexto.

Agregar contexto a través de diferir tampoco tiene sentido, ya que queremos devolver diferentes mensajes de error para distinguir los diferentes tipos de errores. Sin embargo, un try(fn(), "my error message: %w") podría ser útil. Pero incluso entonces, la construcción if err != nil podría seguir siendo preferible, debido a las longitudes de línea más cortas.

Francamente, no quiero un retorno implícito que proporciona try . Si tuviéramos genéricos, preferiría una solución que usara el comportamiento monad-ish en su lugar.

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

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

Para mí, esto es mucho más limpio que el incorporado que se propone actualmente, aunque se requerirían más cambios en lo anterior, ya que tendría una proliferación de métodos que devolvieron (valor, error) y Resultado

La propuesta actual try , que no tiene forma de decorar explícitamente los errores, no satisface mis necesidades. No puedo imaginar usarlo nunca. Francamente, bien podría llamarse code_smell .

Puede que no tenga sentido cambiarlo, porque se está tratando de resolver el problema equivocado.

El código con el que estamos familiarizados no es el manejo de errores.

if err != nil {
  return err
}

Este es el error nil manejo. En ningún punto de este patrón se maneja el valor de un error.

Si tuviera que demostrar esto en un idioma diferente, Ruby.

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

Esto transmite el mismo comportamiento que el código golang. Cuando detectamos que ocurrió una excepción y luego la volvemos a generar. Simplemente lo tiramos a la pila.

En golang, lo return .

¿Dónde está ocurriendo el _ manejo de errores_ real?

Todos hemos tenido experiencias similares del fracaso de este patrón. Por ejemplo, recibir un error file not found y luego pasar mucho tiempo rastreando el _thrower_ original de este error.

Es por eso que creo que la propuesta try (y otras) están fallando. No tenemos un buen patrón para manejar realmente los errores.

He visto err.Error() comprobación de cadenas, afirmaciones de tipo, etc. para inspeccionar realmente el error.
Necesitamos un patrón para esta inconsistencia. Parece que xerrs podría estar resolviendo esto, pero tampoco se siente completo todavía.

Apoyo mantener err! = Nil check como está.

Cada vez que busco en una base de código Go considerable, me pregunto cómo podría reducir parte del texto estándar. Siempre vuelvo a:

  • Esos caminos de código existen de una forma u otra.
  • Incluso si no está obligado a ocultar la ruta de código, dar a las personas la oportunidad de ocultarlo hará que sea el comportamiento predeterminado (porque aparentemente todavía medimos qué tan difícil es usar un lenguaje por recuento de líneas).
  • Si el comportamiento predeterminado oculta las rutas de código, entonces estaría atento a los nuevos errores de "limpieza que faltan".
  • El significado y los patrones de los errores devueltos son lo suficientemente diversos como para que esta propuesta solo capte una parte del problema percibido
  • Si solo se captura una parte, seguramente obtendremos un montón de soluciones
  • Con un montón de soluciones, vendría la tentación de tener algo de magia adaptativa de casos de uso para enrollarlas
  • Que si esto fuera realmente un problema, la gente es libre de crear su propia solución simple o usar algún patrón adoptado en masa. No he visto nada parecido. Quizás no he mirado lo suficiente.

El rastreador de problemas es útil para muchas cosas, pero una cosa para la que no es útil es una discusión detallada de un tema complejo. El rastreador de problemas no proporciona subprocesos y responder a un mensaje específico es incómodo. Dado que no hay una propuesta real aquí, solo una respuesta a otras propuestas, realmente le animo a que lleve esta discusión a la lista de correo de golang-nuts.

Si puedo, creo que esta es la respuesta. Esta nueva propuesta de error entra en conflicto directo con los objetivos del lenguaje.

La razón por la que me encanta golang es por su simplicidad y el uso claro del flujo de control. Una de las cosas que más desprecio de Java es la construcción try throw. Es tan repugnante. Fomenta un terrible manejo de errores. Enviar excepciones a la pila de llamadas es un método horrible y repugnante de manejar el flujo de control. Además, alienta a envolver todo en un cheque gigante y dar por terminado el día en lugar de un manejo explícito y autodocumentado de cada situación de error.

If err! = Nil fomenta un buen manejo de errores, se auto documenta y fomenta una buena documentación en cuanto al caso específico, y honestamente es una de las cosas que más me gustan de go. Hacer que este nuevo flujo de control interrumpa, utilizando retornos y parámetros desordenados, un tanto ambiguos, y una semántica confusa no está en el espíritu del lenguaje que he llegado a adorar.

La verbosidad no es algo malo. La verbosidad innecesaria lo es, pero yo diría que el manejo de errores de go no es innecesario. Es parte del encanto del idioma.

No podría estar más de acuerdo. El manejo explícito de errores es una de las mejores características del lenguaje IMO. Siempre siento que muchos a los que les molesta simplemente no están acostumbrados todavía.

No es bueno que los temas estén separados, pero creo que dos opiniones se fusionan como una sola opinión en este caso.

  1. No nos gusta la nueva sintaxis (prueba o una nueva sintaxis if-err)
  2. De todos modos, no queremos agregar una nueva sintaxis

Los iconos de voto de GitHub no pueden interpretar el segundo.

El manejo explícito de errores en go es una de las razones por las que me encanta golang. No entiendo por qué cualquier desarrollador de Go lo querría de otra manera. Creo que la propuesta de agregar una nueva sintaxis proviene principalmente de personas que se sienten cómodas usando la sintaxis utilizada en otros idiomas. puede que te lleve un tiempo acostumbrarte, pero funciona perfectamente una vez que te acostumbras.

Escribí # 32811 y apoyo más esta propuesta ... Prefiero dejar el manejo de errores solo. Creo que las reacciones de los emoji a esta propuesta dicen mucho.

Personalmente estoy de acuerdo con dejar el manejo de errores como está. Una de las cosas que me gusta de Go es que el lenguaje es mínimo y, en general, tiene una forma de hacer las cosas. Al agregar una nueva sintaxis para el manejo de errores, crearemos un mundo donde x% del código usa el método actual y y% usa el nuevo método. Esto, entre otros temas ya discutidos, creará bases de código inconsistentes. Personalmente, no creo que el valor de la nueva sintaxis de manejo de errores valga las compensaciones, ya que considero que la sintaxis existente es suficiente / suficiente.

Como alguien que es más nuevo en Golang, una de las cosas que encuentro refrescante sobre el lenguaje es el manejo explícito de errores. He trabajado bastante en Java, Ruby, Python y Node, y lidiar con los errores es mucho más oneroso que en Go. Preferiría ver el "camino" claro de los errores, a que me lo implique alguna construcción del lenguaje que lo haga más vago.

ˋReturn ... if ... ˋ sugerencia de @andreynering es en realidad bastante inteligente en mi humilde opinión. Mantiene el código explícito (sin interrupción del flujo de control oculto) mientras reduce el texto estándar (una sola línea).

De acuerdo, deja if err != nil solo.

Prefiero el formato actual. Es un patrón claro y fácil de enseñar. Poner al día a los nuevos ingenieros es sencillo, ya que pueden aprender un patrón sencillo y repetirlo. También pide a los usuarios que al menos consideren el error en el contexto actual, asegurándose de que al menos el ingeniero reconozca que puede ocurrir un error aquí y necesito pensar qué hacer.

Escribí # 32804 y preferiría que las cosas NO cambien. Si su código es largo, es porque hace muchas cosas. Si tiene mucho código de manejo de errores, es porque está haciendo un buen trabajo manejando todos sus casos.

Por favor, no agreguemos cosas solo por agregar cosas.

Disfruto de la simplicidad del manejo de errores tal cual.

Expect es solo un anagrama de except, y prefiero no usarlo. Gracias por comenzar con esto.

Por favor, no cambies mi santo grial.

Hubo una abrumadora retroalimentación de la comunidad que solicitó un manejo de errores más simplificado (de la encuesta anual). El Go Team ahora está abordando ese problema.

@icholy Claro, pero las propuestas actuales dejan mucho que desear. Todos parecen ofuscar el manejo de errores, volver a más implementaciones de estilo try / catch / finalmente, sacar el manejo de errores fuera de contexto o de otra manera hacerlo más complicado. Dado que se supone que Go es un lenguaje simple, creo que muchos de nosotros esperábamos una opción simple. No he visto ninguno que me guste personalmente, así que creo que la mejor opción es mantener el patrón actual.

Una queja fue tener que escribirlo, pero prácticamente todos los editores tienen atajos para insertar fragmentos de código, por lo que realmente no es un gran problema. Quizás sea mi propia experiencia haber usado Go desde la versión anterior a 1.0, pero resulta que me gusta la simplicidad y no me importa la redundancia.

@kevineaton, ¿crees que try es complicado?

Estoy de acuerdo con esto completamente. Ni siquiera estoy convencido personalmente de que tengamos que hacer algo; estoy de acuerdo en que los cheques if err != nil parecen incómodos a primera vista, pero no he visto nada propuesto que realmente resuelva el problema sin violar ampliamente las mismas cosas que se hacen. popular para.

@icholy después de pasar diez años escribiendo Java y Python antes de Go, creo que puede ser. Creo que te encuentras con la captura de excepciones de Pokémon o el encadenamiento de múltiples excepciones y, de lo contrario, introduces aún más gastos generales y repetitivos. No volvería a ese estilo de manejo de errores si pudiera evitarlo, ya que casi siempre me genera dolores de cabeza y confusión, ESPECIALMENTE cuando se enseña. También enseño ciencias de la computación además de mi trabajo diario como arquitecto de software, por lo que estoy predispuesto a enseñar a nuevos desarrolladores y ser mentor. Elegiría Ir y es un manejo de errores simple en lugar de un manejo de errores potencialmente más complicado o matizado cualquier día.

El rastreador de problemas es útil para muchas cosas, pero una cosa para la que no es útil es una discusión detallada de un tema complejo.

No es esa la verdad. Pero aquí estamos.

if err != nil no desaparecerá si se agrega try . Creo que try agregará algo de claridad a las rutas de código que son pesadas en el reenvío de errores o donde muchos errores diferentes pueden resumirse fácilmente en un manejador de errores diferido. . Realmente no veo cómo try alienta a no manejar errores mucho más que un montón de if-err-return-err vacíos. Es fácil ignorar el manejo real de los errores independientemente de si try está ahí o no. Creo que try es una de las mejores sugerencias para el manejo de errores hasta ahora porque parece que será fácil de leer el código que lo está usando.

Mis dos centavos no solicitados, simplemente no se siente muy "Go". Es demasiado mágico y optamos por construcciones implícitas sobre explícitas.

De las preguntas frecuentes de Go

¿Por qué Go no tiene el operador?:?
_No hay una operación de prueba ternaria en Go. Puede utilizar lo siguiente para lograr el mismo resultado: _

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

¿La razón ?: está ausente de Go es que los diseñadores del lenguaje habían visto la operación utilizada con demasiada frecuencia para crear expresiones impenetrablemente complejas. La forma if-else, aunque más larga, es sin duda más clara. Un lenguaje solo necesita

@ianlancetaylor

El rastreador de problemas es útil para muchas cosas, pero una cosa para la que no es útil es una discusión detallada de un tema complejo. El rastreador de problemas no proporciona subprocesos y responder a un mensaje específico es incómodo. Dado que no hay una propuesta real aquí, solo una respuesta a otras propuestas, realmente le animo a que lleve esta discusión a la lista de correo de golang-nuts.

Puede responder a un mensaje específico. Yo solo respondí al tuyo. :)

Dado que aquí no hay una propuesta real, solo una respuesta a otras propuestas,

Una propuesta para mí significa un llamado al cambio. Este tema en particular es anti-cambio. ¿Propone que creemos una propuesta para _no_ cambiar el manejo de errores? Creo que el sistema de propuestas es excelente, pero deja el statu quo infrarrepresentado.

después de pasar diez años escribiendo Java y Python ... también enseño informática además de mi trabajo diario como arquitecto de software

@kevineaton, ¿has terminado de

Este problema funciona como una encuesta de larga duración en un lugar semioficial donde básicamente cualquiera puede votar fácilmente a favor o en contra de las propuestas.

No cambiar el idioma para eliminar if err != nil es una propuesta perfectamente cromática que básicamente no necesita detalles adicionales. No estoy seguro de cuál es el problema. No, no es demasiado largo y difícil de asimilar. Eso no lo hace malo, malo o insuficiente.

+1, si no hay nada mejor, lo bueno será una muy buena información de seguimiento de pila (sin elementos de baile de marcos), supongo que x/errors ya lo lograron, pero me encantaría algo rápido en un futuro cercano, como marcar func s usando la palabra clave throws que devolvería una palabra clave error + try , evitando el sombreado de var de error (que personalmente odio), algo como esto:

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

anInt, err := try a()

@icholy Eso fue increíblemente innecesario. Este es un lugar de discusión y se supone que la comunidad de Go es una comunidad acogedora. No hay lugar para ese tipo de comentarios. Creo que Sócrates tuvo algo que decir sobre los insultos en un debate.

El manejo de errores actual es propenso a errores humanos. Es bastante fácil olvidarse de comprobar err en este momento. Si ya hay comprobaciones en el alcance (y la mayoría de las veces las hay), el compilador no terminará con unused variable . El manejo de errores debe ser estricto - usted _ un error o verificarlo - no debería ser posible disparar con las piernas.

@kevineaton, ¿crees que try es complicado?

try es un olor a código. Fuerza la sangría entre todo su bloque de código en lugar de solo en un lugar. Además, la naturaleza "burbujeante" del manejo de excepciones crea un comportamiento no determinista de facto en el código y múltiples puntos de salida.

La belleza de usar múltiples valores de retorno en lugar de try es que hay un valor para verificar cuando su función está terminada y un punto de salida de su función (a menos que, por supuesto, use declaraciones de guardia u otros retornos explícitos).

try bloques

@fillest Si bien haría que el código fuera un poco menos legible, creo que esto sería un valor agregado en términos de seguridad / manejo explícito de errores. Si miras hacia atrás en los objetivos originales sobre cómo manejamos los errores en Go, creo que sería una buena iteración para evitar la clase de error que citas sin dejar de perseguir el espíritu explícito de ser bueno.

El manejo de errores actual es propenso a errores humanos. Es bastante fácil olvidarse de comprobar err en este momento. Si ya hay comprobaciones en el alcance (y la mayoría de las veces las hay), el compilador no terminará con una variable no utilizada. El manejo de errores debe ser estricto - usted _ un error o verifíquelo - no debe ser posible disparar con las piernas.

@fillest El cambio propuesto al manejo de errores hace que "disparar a pierna" sea más fácil y los errores sean más pronunciados porque pueden manejarse con pereza.

Dejé de usar Go debido a la falta de genéricos, propensión a repetición, GC, falta de límites de recursos / contabilidad y carga de trabajo generada desde PHP novatos que no entendían lo que hace un compilador. Haskell, C # y otros resolvieron el manejo de errores bastante bien ... la propuesta de Go 2 se ve bien si tiene un manejo de casos explícito como antes (no estoy seguro).

El manejo de errores está en el corazón de la programación. Modelar la lógica empresarial (por compleja que sea) siempre es más simple que responder a las condiciones no válidas que genera esta lógica. Simplemente reenviar un error es un olor a código. Deseo que Go no fomente este comportamiento, pero promueva patrones de gestión de errores. Los principiantes a menudo se confunden con todo este código de manejo de errores porque no se han dado cuenta de lo central que es la gestión de errores.

Totalmente de acuerdo, ya que el try incorporado no ayudará a envolver los errores y agregarles información ni siquiera por un momento.

Antes de reescribir con 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")
}

Imagínese lo que será después de reescribir con try .

Dado que try ya actúa como una función de 1 argumento al encerrar su argumento entre paréntesis, podría aceptar un segundo argumento que es el código de ajuste de error.

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

Donde el err tendría que introducirse implícitamente (de forma higiénica). Entonces, si try se usa como una función de 1 argumento, simplemente devolvería su error sin cambios.

Estoy de acuerdo, lo único "azúcar sintáctico" que podría hacer que el manejo de errores sea un poco más simple es permitirnos hacer algo como lo siguiente cuando tenemos múltiples retornos de nuestras funciones ... los guiones bajos serían valores predeterminados de cualquiera que sea el tipo de retorno

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

@sorenvonsarvort no me parece tan malo:

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())

Según tengo entendido, también puede usar if err != nil { ... } si está más claro para esa sección de código en particular.

try brilla en otros casos. Imagina algo más como:

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

Un código como el anterior puede ser mucho más limpio que si tuvieras que esparcir bloques if err != nil . Go tiene que ver con la "legibilidad lineal", así que creo que try funciona bien en ese sentido.

Hubo una abrumadora retroalimentación de la comunidad que solicitó un manejo de errores más simplificado (de la encuesta anual). El Go Team ahora está abordando ese problema.

Esta es una minoría vocal y apuesto a que una buena parte de ellos ni siquiera usan Go

@sirkon ¿en qué estás basando esa declaración?

@sorenvonsarvort no me parece tan malo:

Un código como el anterior puede ser mucho más limpio que si tuvieras que esparcir bloques if err != nil . Go tiene que ver con la "legibilidad lineal", así que creo que try funciona bien en ese sentido.

En Rusia lo llamamos «экономия на спичках». Utilice el traductor de Google para entenderlo.

Para aquellos en este hilo que aún no lo han hecho, recomendaría leer este comentario sobre el problema de la propuesta original try . Analiza las mejores prácticas generales del contexto de error y cómo podría expresarse con try .

Creo que quizás el contexto de error se ha vuelto un poco dogmatizado en la comunidad de Go. Sé que personalmente me he enamorado de esto y sobre-contextualicé mis errores, lo que resultó en mensajes muy largos, repetitivos y difíciles de leer. Hay muchos matices con respecto a cuándo contextualizar los errores y cuándo no.

Me gusta que try sea ​​básicamente un atajo y reduce algunos códigos repetitivos. Pero perdemos la capacidad de envolver los errores con información adicional. Sin embargo, el siguiente cambio podría solucionarlo:

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

se convierte en

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

Por supuesto, si necesita hacer mucho más que eso, la forma "completa" de hacer una verificación err != nil todavía está disponible.

Estoy de acuerdo con esto, pero respetaré la solicitud del equipo de go de tener más experiencia con el cambio antes de tener una opinión final.

Pero mi experiencia preliminar con el cambio parece respaldar que es realmente innecesario. Tengo 2 programas del "mundo real" con aproximadamente 10k líneas cada uno y ejecutando tryhard en ambos programas que ninguno de ellos se beneficiaría de este cambio. Esto se explica fácilmente por el hecho de que ambos siempre agregan contexto a los errores. Tengo otros programas de "juguetes" más pequeños en Go y tryhard encontré 1 caso en el que podría haber usado try en uno de ellos, pero eso es todo.

Admito que otras personas pueden tratar los errores de manera diferente a mí y admito la posibilidad de que try se pueda usar de manera positiva. El propio código fuente tryhard tiene algunos casos consecutivos de return err , que si se usara try no creo que la legibilidad se vea comprometida tanto. Pero solo temo los usos indebidos, porque afectarán la legibilidad. Aquí se proporciona un buen ejemplo. Y luego determinar qué es un buen uso o no será una historia completamente diferente.

Además, me gusta cómo la gente normalmente puede leer el código go incluso si no programan go ellos mismos. Esto hará que tengan que aprender la magia que hace try , especialmente porque hace algo diferente a los otros try que han visto en otros idiomas. Esto también es cierto para las personas nuevas que llegan al idioma, es solo otra característica que tendrán que aprender en un idioma que se enorgullece de ser simple al tener "solo las características que necesita".

Vamos a esperar y ver. Experimentaré más con este cambio, pero no estoy seguro de que cambie mi posición.

Hubo una abrumadora retroalimentación de la comunidad que solicitó un manejo de errores más simplificado (de la encuesta anual). El Go Team ahora está abordando ese problema.

@icholy Como dije, me gusta agregar contexto a los errores. En ese sentido, "un manejo de errores más simplificado" para mí significa mejores formas de proporcionar ese contexto y obtener información de él. Por ejemplo, con todo el contexto que agregué a mis errores, debería ser trivial preguntar al "contexto" si el error fue causado por un tiempo de espera. Pero no lo es. Normalmente tienes que recurrir a usar pkg/error , hacer tus propias "estructuras de error" y / o hacer métodos para trabajar con ellas que dependiendo de la implementación se pueda recurrir a hacer búsquedas de cadenas. Prefiero ver algo que me salve de hacer estructuras y métodos completos que cosas que me ahorren un solo if muy ocasionalmente en el mejor de los casos. Y como se dijo anteriormente, ¿puede realmente llamarlo "manejo de errores" cuando este cambio básicamente proporciona una forma más conveniente de no manejar realmente el error?

¿Crees que try es complicado?

De forma aislada, try no es complicado, pero los cambios de idioma no se consideran de forma aislada. Considerar:

  • Aumento de la carga cognitiva al aprender la semántica de una nueva función incorporada
  • Reducción en la legibilidad debido al uso de try porque es más corto, en los casos en que se debería haber usado if err != nil {return ... errors.Wrap() } lugar

Me hago eco de los sentimientos anteriores de que la simplicidad (tener una única forma de comprobar los errores) es más importante que una forma breve de comprobar los errores.

¿No es que la forma de manejar un error es a través de esa declaración global y si esto es algo que debe ignorarse, entonces, cómo se manejan las situaciones de pánico? Apoyaría un mejor manejo de errores, ya que otros paradigmas de programación manejan errores hoy, pero no los ignorar.

¿No veo ningún problema con la propuesta try ?
Si desea utilizar un comportamiento antiguo o necesita manejar el error de otra manera, ¿por qué no reutilizar el método antiguo? Nadie que te clava un cuchillo en el cuello presiona ¿usas la sintaxis try ?
try sintaxis de

¿Qué pasaría si gofmt se cambiara de modo que una declaración si los bloques permanecieran en una línea?

De esa manera, podríamos haber envuelto los errores que solo necesitan una línea para manejar en lugar de tres en la mayoría de los casos. Esto reduciría el espacio vertical ocupado por el manejo de errores y haría parecer que el manejo de errores no ocupa más de la mitad del espacio vertical en la función promedio.

Así es como se vería:

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

Creo que quizás el contexto de error se ha vuelto un poco dogmatizado en la comunidad de Go. Sé que personalmente me he enamorado de esto y sobre-contextualicé mis errores, lo que resultó en mensajes muy largos, repetitivos y difíciles de leer. Hay muchos matices con respecto a cuándo contextualizar los errores y cuándo no.

La envoltura de errores y el marco de pila / impresión de errores serían mucho más sencillos. Creo que habrá una buena solución para eso con el tiempo, pero no ahora. Personalmente, preferiría esperar con la introducción de funciones go2 más potentes hasta que se resuelva el polimorfismo paramétrico porque puede ser potencialmente útil al diseñar el resto de las funciones.

Estoy totalmente de acuerdo con dejarlo como está. Es un poco demasiado detallado, pero es bastante sencillo de seguir.

Si pudiera reducir

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
}

a algo como

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

tal vez también sería genial sin cambiar demasiado el idioma en mi humilde opinión.

@henvic Creo que el problema con eso es que está asumiendo que desea manejar los tres errores de la misma manera. Ir te obliga a pensar en cómo manejar cada error de forma individual. Tener verificaciones de errores por separado deja esto en claro. Agregarlos en uno hace que el desarrollador tenga que retroceder cognitivamente y verificar, ¿deberían estos errores _ realmente_ ser tratados de la misma manera? Creo que con esta propuesta perdemos claridad y forzamos a pensar en los errores por unas pocas pulsaciones de tecla menos.

@sanbornm , tienes razón. Estoy de acuerdo.

Este check también podría ser un operador checkNonZero que solo tomaría un argumento de retorno y regresaría al primer valor distinto de cero (como nulo). Pero además de ser demasiado vago, y afectar aún más el idioma. Incluso solo daría una pequeña pista de que no debería usarlo si espera, digamos, io.EOF. Otra posibilidad sería, quizás, contar con go vet para al menos informarle de los casos más comunes (como io.EOF al leer de una tubería) ... pero esta idea no suena bien en todo para mi.

¿No veo ningún problema con la propuesta try ?
Si desea utilizar un comportamiento antiguo o necesita manejar el error de otra manera, ¿por qué no reutilizar el método antiguo? Nadie que te clava un cuchillo en el cuello presiona ¿usas la sintaxis try ?
try sintaxis de

Todos vivimos en comunidad. Todos los días trabajo con mucho código escrito por otras personas. Así que el cambio de idioma me afectará incluso si no lo uso.

En proyectos grandes, los peores registros son generados por una función que olvidamos que alguien escribió, que llama a una biblioteca que llama a una biblioteca que llama a una biblioteca que arrojó un new Exception() genérico que nada capturó hasta que un controlador de excepción "Pokémon" lo hizo. y registró un error genérico. Los segundos peores son los mismos, pero con un rastro de pila inescrutable de varios cientos de líneas, con el que supongo que eventualmente podemos descubrir la causa (afortunadamente, solo buscando github.com/<us>/<ourproject> encuentra la mayor parte de la información relevante, pero a veces hay mucho). A pesar de su nombre, las "Excepciones" son lamentablemente poco excepcionales en los grandes proyectos de Java.

Mientras tanto, incluso cuando hay mucho contexto redundante, las cadenas de error Go simples como "narf: Error unpoiting the zort: foo: Unexpected bar in baz: {\"ork\": \"morpork\"}" han sido (en mi experiencia) típicamente muy fáciles de interpretar, siempre y cuando hayamos sido diligentes en incorporar el contexto importante en algún lugar de el valor de error real. Si resulta que falta un contexto importante, también suele ser bastante obvio. La "solución" en esos casos es agregar más contexto y esperar otro error, por lo que no es perfecto, pero a fin de cuentas, sigo prefiriendo esto a desplazarme por los seguimientos de la pila y / o confiar en las dependencias de las dependencias de mis dependencias para "lanzar "o" levantar "mensajes de error cuerdos. Realmente aprecio cómo el nombre panic() parece evitar que la mayoría de los desarrolladores de Go implementen tan generosamente lo que es esencialmente la misma característica de idioma. No hagamos que error y panic lo mismo después de todo.

Ocasionalmente me he encontrado con situaciones en las que una función tenía una docena de modos de falla y la gran mayoría de ellos merecían el mismo mensaje de error. La repetición realmente no me molesta, pero generalmente hay alguien más en mi equipo a quien molesta, por lo que nos comprometemos declarando un cierre al comienzo de la función para manejar esos errores comunes.

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)
  }
}

Lo cual, sin duda, sigue siendo una solución imperfecta en algunos aspectos. Pero me gusta que al hacer esto sea muy fácil para los futuros desarrolladores entender cuándo y qué devuelve foo , sin que el flujo de control salte demasiado.

Si de alguna manera este "problema" de repetición es extremadamente común en numerosos paquetes en lugar de (como yo lo veo normalmente) confinado a un puñado de funciones irreductiblemente complejas en un paquete irreduciblemente complejo, un "functor" de todo el proyecto probablemente podría usarse para un final similar, y (suspiro) si un concepto de tipos paramétricos termina agregándose al lenguaje, podría ocultar aún más detalles detrás de un error-handling-factory-factory sin necesidad de depender de try / catch.

@thomasf

La envoltura de errores y el marco de pila / impresión de errores harían mucho más sencillo

Estoy de acuerdo.

Personalmente, preferiría esperar con la introducción de funciones go2 más potentes hasta que se resuelva el polimorfismo paramétrico porque puede ser potencialmente útil al diseñar el resto de las funciones.

Algunas personas en la propuesta try discutieron la espera de genéricos, pero no me queda claro cómo el polimorfismo paramétrico haría que la propuesta try diferente. Ya está integrado, por lo que no se limita a las limitaciones de lo que podemos expresar en el idioma.

@icholy

Hubo una abrumadora retroalimentación de la comunidad que solicitó un manejo de errores más simplificado (de la encuesta anual). El Go Team ahora está abordando ese problema.

Solo para responder a esto; también había una mayoría en el Reino Unido a favor del Brexit. Claro, la UE también trae consigo algunas desventajas a las que respondió el público en general. Sin embargo, una vez que se plantearon todas las alternativas, parecía que permanecer en la UE no sería tan malo después de todo.

Ahora bien, no es mi intención politizar esto en absoluto, y es posible que no esté de acuerdo con lo anterior. Pero lo que quiero mostrar es que incluso cuando una mayoría inicialmente considera que algo es una molestia, aún puede ser la mejor solución una vez que se hayan examinado todas las alternativas.

No tengo muchas opiniones sobre el manejo de errores, pero _podría_ ser un argumento para dejar las cosas como están.

En un entorno de codificación profesional, aprovechamos las prácticas actuales de manejo de errores para anotar sistemas de rastreo y decorar registros. Como acotación al margen, el retorno implícito es más bien como usar el pánico en una función de biblioteca exportada, ya que oscurece la legibilidad inmediata del control de flujo.

@icholy

Hubo una abrumadora retroalimentación de la comunidad que solicitó un manejo de errores más simplificado (de la encuesta anual). El Go Team ahora está abordando ese problema.

Solo para responder a esto; también había una mayoría en el Reino Unido a favor del Brexit. Claro, la UE también trae consigo algunas desventajas a las que respondió el público en general. Sin embargo, una vez que se plantearon todas las alternativas, parecía que permanecer en la UE no sería tan malo después de todo.

No es necesario que consideres la afirmación de esta persona en serio: el recuento de emojis muestra que a las personas generalmente no les gusta la propuesta try y, en general, les gusta esta propuesta de “déjalo como está”.

PD: en mi práctica, la gran mayoría de las personas a las que no les gusta Go en su dominio principal (servicios de red, utilidades CLI) ni siquiera lo han usado. Por eso preferiría ignorar sus opiniones.

Necesitamos opciones mejores y menos controvertidas que la propuesta try .
No veo la urgencia de soluciones apresuradas.

@velovix Creo que odio el polimorfismo paramétrico más que intentar / detectar el "manejo" de errores, pero si se convirtiera en una característica de lenguaje, podría ver algunas formas en que evitaría la necesidad de otra característica de lenguaje incorporada.

Por un lado, cuando el código que la gente no quiere repetir es repetitivo como:

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

Entonces, alguna combinación de funciones paramétricas, inferencia de tipos y patrones de diseño de objetos de estilo _tal vez_ u _opcional_ reduciría directamente (je) el texto estándar sin recurrir a extrañas estrategias de flujo de control no lineal:

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)

En mi opinión, todo esto sería mucho, mucho peor que Go1. Pero es mejor que tener esta _plus_ prueba / captura de palabras clave en el idioma.

Honestamente ... Tal como están las cosas en este momento, creo que mis cambios de "ruptura" favoritos para Go2 solucionarían todos los pequeños inconvenientes en Go1, como que los valores predeterminados de net/http son globales compartidos mutables anidados dentro de globales compartidos mutables (solo hacer que el cleanhttp Hashicorp sea estándar, básicamente), o (*time.Timer).Reset() con un valor de retorno inútil que solo debe conocer, o todo el paquete syscall . Go3 se puede liberar casi inmediatamente después de eso con cualquier tumor que la gente quiera que crezca en él; No veo por qué los cambios grandes y pequeños deben realizarse en una sola versión.

Estoy a favor de try ... cuando se usa con moderación. Sospecho que los proyectos populares agregarán pautas sobre cuándo / si están bien con el uso de try, y que los proyectos pequeños / nuevos / de una sola persona en algún momento sufrirán errores deficientes y, por lo tanto, falta de uso debido a try ing con demasiada frecuencia. Esos proyectos morirán o serán arreglados o bifurcados.

Realmente no veo que la adición de try al lenguaje sea tan horrible. Si los temores del peor de los casos de las personas resultan fundados, su uso estará mal visto. Las personas sin experiencia lo usarán indiscriminadamente, mientras que otras no. No es el fin del mundo.

Si se agrega try , probablemente lo usaré en algunos casos. Esos casos son, en los que se devuelve un error, pero creo que es tan increíblemente improbable que realmente haya un error que no veo el sentido de agregar contexto, y simplemente devuelvo el error tal como está. Por ejemplo, si acabo de crear un sistema de archivos que llena un disco que sé que es de 1 TB, puedo estar seguro de que hay espacio para crear un archivo de 1 kb o un directorio. Si eso falla, no quiero ignorar el error; podría indicar un error en otro lugar, falla de hardware, etc. Sin embargo, no vale la pena esforzarse en anotar cada error increíblemente improbable.

No me gusta cómo try (..) es simplemente poner un par de paréntesis / corchetes más en la pila mental de un codificador para pensar en cuando escribe. ¡Y durante el mayor tiempo que puedo imaginar!

Entonces esto es mejor:
valor, err: = foo ()
devuelve err si err! = nil

Pero aún así, esto es tan común. Así que me gustaría que algo como esto fuera posible de alguna manera:

valor, comprobar err: = foo ()

Si Go quiere ser un lenguaje legible, se debe minimizar la capacidad de realizar pensamientos que sean difíciles de leer o comprender.

Si Go quiere tener un buen manejo de errores, debería fomentar que los errores tengan contexto adicional a medida que ascienden en la pila de llamadas. El requisito de utilizar aplazar para manejar errores parece confuso. ¿Qué pasa si su controlador de errores tiene un error? Los diferidos se ejecutan en orden de pila, ¿necesitamos declarar los manejadores al revés?

Si las declaraciones son sencillas y dejan poco espacio para la ambigüedad aquí. Siento que try está resolviendo una liendre en lugar de un problema de ingeniería real. Me gusta esta propuesta porque permite que la mayoría silenciosa finalmente haga una declaración sin comprender completamente todas las facetas de estas complejas propuestas.

@icholy Por favor, sea cortés. Tenga en cuenta el Código de conducta de Gopher: https://golang.org/conduct. Gracias.

Todos: además de mi comentario anterior (https://github.com/golang/go/issues/32825#issuecomment-506740412), tenga en cuenta https://golang.org/wiki/NoPlusOne. No es útil hacer un comentario que diga poco más que "Estoy de acuerdo" o "No estoy de acuerdo". En su lugar, usa los botones emoji. Gracias.

@sanbornm

(Estoy de acuerdo en que es posible responder un mensaje; dije que era incómodo, no imposible. Y mi punto sobre el enhebrado sigue en pie, en el sentido de que este mini-hilo se pierde en una tormenta de otros comentarios).

Una propuesta para mí significa un llamado al cambio. Este tema en particular es anti-cambio. ¿Propones que creemos una propuesta para no cambiar el manejo de errores? Creo que el sistema de propuestas es excelente, pero deja el statu quo infrarrepresentado.

No es necesario crear la propuesta A diciendo que la propuesta B no debe ser adoptada. En su lugar, vote en contra de la propuesta B. Para una discusión detallada de la propuesta B, use esa propuesta o la lista de correo.

(Entiendo que la propuesta B en este caso está bloqueada; el hecho de que esta propuesta tenga 77 comentarios en menos de un día muestra por qué. Este nivel de discusión simplemente funciona mejor en una lista de correo que en el rastreador de problemas).

@ianlancetaylor

(Estoy de acuerdo en que es posible responder un mensaje; dije que era incómodo, no imposible. Y mi punto sobre el enhebrado sigue en pie, en el sentido de que este mini-hilo se pierde en una tormenta de otros comentarios).

Bastante justo, eso tiene sentido. Las listas de correo son geniales, pero personalmente me resulta más fácil contribuir a través de GitHub en este caso. No tengo mucho que decir aparte de que el manejo de errores actual es excelente y deseo que siga siendo el mismo. Los emojis / votos son geniales para esto. Probablemente no desee que 100 personas escriban "Por favor, deje el manejo de errores solo" en la lista de correo donde 100 "votos" serían suficientes.

Debido a que este problema está bloqueado, ya no se puede "votar" con Emojis. Por eso creo que este problema se creó en primer lugar.

Un punto lateral, pero relacionado, la gestión de la dependencia no se manejó bien. Dep funcionó muy bien y se eligió go mod (de lo que parecía) de la nada [1]. Entiendo que por eso se creó el sistema de propuestas. Simplemente siento que el sistema de propuestas en este caso podría representar insuficientemente a la comunidad si los problemas están bloqueados y se nos dice que vayamos a las listas de correo.

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

Editar: El equipo de Go y la comunidad en su mayor parte hacen un trabajo increíble al escuchar los comentarios de la comunidad. Agradezco todo el trabajo que implica. Las encuestas Go son un gran ejemplo.

@sanbornm

Dep funcionó muy bien

Necesito estar en desacuerdo aquí. Los módulos Go finalmente resolvieron ese mal conocido problema de "gobindata" con su almacenamiento en caché persistente por https://proxy.golang.org

Ese tipo de departamento ni siquiera se dio cuenta del problema y en su lugar estaba jugando con un elegante "aseguramiento" a través de VCSes.

@sirkon Esto está un poco fuera de tema, pero no necesita nada de eso si trabaja con departamentos de proveedores como lo estaba haciendo Dep.

Tal como está, creo que preferiría dejar las cosas como están a menos que se agreguen más restricciones, como 1 declaración de prueba por línea. La razón es considerar este ejemplo de la propuesta : parece bastante inofensivo info := try(try(os.Open(file)).Stat()) pero filtra los identificadores de archivos más allá del alcance de lo que haría el flujo de control normal. Creo que veremos un aumento en las fugas de recursos de archivos con implementaciones de io.Closer u otra función de limpieza que la gente puede evadir en la búsqueda de un código más compacto.

Tal vez algunas personas consideren que es intrascendente porque f ya no estará activo y, por lo tanto, será elegible para GC de inmediato y, en algún momento, el finalizador se asegurará de que f esté cerrado. Creo que cambia las convenciones claras anteriores (compatibles con linter) de usar diferir hoy que están vinculadas a un bloque de funciones. Cuando se sale del bloque de funciones, se libera el recurso. Depender del recolector de basura no ofrece garantías de que no agotará los recursos (los valores predeterminados del límite de manejo de archivos abiertos típicos pueden estar entre 1k y 4k), que se excede fácilmente con una ruta de archivo simple.

En resumen, creo que esta sintaxis, tal como se implementó, ofrece un riesgo sutil en la administración de recursos en Go, ya que carece de ctor / dtor y se basa en una maquinaria GC de nivel inferior muy alejada de los bloques de código para evitar fugas de recursos. Convertir algo que parece inofensivo en una condición de error potencial (demasiados archivos abiertos).

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
}

editar:
Para las restricciones, creo que si solo fuera válido en el lado derecho de una asignación, sería mejor que decir 1 por línea, ya que a, b := try(get("a")), try(get("b")) es lo suficientemente razonable. Pero aún deja la capacidad de hacer try(os.Open(name)).Stat() , que si hiciera que try () sea nulo, pero solo cuando no esté en el RHS de una asignación, se quedará con algo que no es muy funcional como en todos.

@cstockton ¡ guau gran descubrimiento!

Rust en realidad tiene una macro similar ( ? si no recuerdo mal) que hace exactamente lo que este try pretendía hacer, pero tienen el raii adecuado allí, por lo que no es un problema en ese idioma y una enorme agujero en nuestro caso

@sanbornm sí, mantener la mitad de Internet en su repositorio parece una gran idea.

Finishing Touch

Como alguien mencionó una utilidad que contaría la cantidad de lugares donde try me ahorraría tiempo / esfuerzo , decidí ejecutarla en mis proyectos de trabajo más grandes, además de todo lo demás en mi antiguo directorio GOPATH GitHub.

| Proyecto | LOC * | probar candidatos |
| ---------- | ------ | ---------------- |
| cal1 | 2047 | 3 |
| pump1 | 1030 | 0 |
| docs1 | 4576 | 8 |
| hugoutil | 604 | 1 |
| todo lo demás | 8452 | 23 |

  • Vaya solo al código, excluidos los comentarios, de acuerdo con la utilidad cloc .

Tenga en cuenta que el contenido de "todo lo demás" incluye trucos rápidos y código que escribí cuando estaba aprendiendo Go.

Mi conclusión general es que al menos para mí, la propuesta try no ayudaría a agilizar mi manejo de errores en ningún grado que valga la pena.

La principal razón por la que me encanta ir es que su especificación restringe a los codificadores a un pequeño subconjunto de sintaxis disponible para otros lenguajes. Debido a que es un conjunto de funciones tan pequeño, es fácil aprender todo el conjunto de funciones. Un futuro desarrollador probablemente pueda ver mi código y saber lo que hice. Cada cosa nueva que se agrega al lenguaje disminuye la posibilidad de que el futuro desarrollador sepa esa cosa. El extremo de la pendiente resbaladiza es un lenguaje cuya complejidad dificulta asimilar, como C ++ o scala.
Me gustaría no ver adiciones de sintaxis para ir 1. Colóquelas en go 2 en su lugar.

@miekg , agregue este enlace https://github.com/golang/go/issues/32825#issuecomment -506882164 en la propuesta. El ejemplo descalifica por completo la idea de esta palabra clave try reciente.

image

Estoy totalmente de acuerdo con dejarlo como está. Es un poco demasiado detallado, pero es bastante sencillo de seguir.

Si pudiera reducir

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
}

a algo como

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

tal vez también sería genial sin cambiar demasiado el idioma en mi humilde opinión.

Si hablaba de un tipo "Quizás", primero requiere el tipo de variante.

Mantengamos if err != nil , funciona, está claro, en realidad no es tan detallado y tiene sentido en el flujo del código. A medida que lee el código con esta construcción, sabe lo que va a hacer.
Quedémonos, no agreguemos try

Cuando leo el código, quiero que las líneas que hacen el trabajo sean claramente legibles, sin o con un mínimo de manejo de errores.

Las 3 letras 'err' en el mismo nivel son aceptables para mí. No me gustaría que alguna función de 'verificación' envolviera el código importante, porque el código importante estaría en un segundo nivel (¿recuerdas lisp?), Y no me gustaría 'probar' una línea antes, porque el código importante vendría sangrado y en la segunda línea.

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

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

Con este código, puede leer el flujo del caso sin error leyendo las primeras líneas de los bloques (como leo los títulos de los documentos cuando tengo que leerlos rápidamente)

Como alguien mencionó una utilidad que contaría la cantidad de lugares donde try me ahorraría tiempo / esfuerzo , decidí ejecutarla en mis proyectos de trabajo más grandes, además de todo lo demás en mi antiguo directorio GOPATH GitHub.

Proyecto LOC * prueba candidatos
cal1 2047 3
bomba1 1030 0
docs1 4576 8
hugoutil 604 1
todo lo demás 8452 23

  • Vaya solo al código, excluidos los comentarios, de acuerdo con la utilidad cloc .

Creo que try es más necesario en programas más grandes. Solo dibujando de la memoria, creo que los programas con tamaños de LOC de alrededor de 15-20k y superiores los necesitan más porque es entonces cuando puede comenzar a obtener capas que solo necesitan pasar errores porque están adecuadamente especificados y manejados en un sistema cerrado por tanto el lado emisor como el receptor. Sin embargo, depende mucho del tipo de programa que sea. Probablemente tampoco usaría probar mucho en programas más pequeños

Creo que es más necesario intentarlo en programas más grandes.

Buen punto. Probé tryhard en heptio / contour, 28.7k líneas de texto fuente, tryhard encontró 12 sustituciones.

Creo que es más necesario intentarlo en programas más grandes.

Buen punto. Probé tryhard en heptio / contour, 28.7k líneas de texto fuente, tryhard encontró 12 sustituciones.

¡GUAU! 12 vs 28.7K líneas, ¡esto realmente necesita una palabra clave dedicada!

Bueno, estoy más interesado en tu punto de vista sobre esto :

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

Creo que es más común si su programa es un poco más monolítico y no forma parte de una integración de servicios entre muchos servicios. Cuando busco fmt.errorf o errors en github en ese repositorio ( heptio/contour ) solo hay muy pocos resultados, por lo que es difícil obtener una descripción general rápida ... Pero como yo dijo que probablemente varía mucho de un programa a otro, incluso para programas más grandes.

Digamos que tiene un solo programa que no usa muchas bibliotecas externas. Luego, puede tener un AuthorizationError específico (y sabe que todos los errores devueltos son lo suficientemente específicos con cualquier error io ya manejado y empaquetado) que ya contiene sus metadatos de usuario y se puede propagar sin cambiar algunas capas sin muchos cambios en las cosas que realmente necesitan para manejarlos hasta la capa de solicitud.

Creo que es más común si su programa es un poco más monolítico y no forma parte de una integración de servicios entre muchos servicios. Cuando solo busco fmt.errorf o errors en github en ese repositorio, solo hay muy pocos resultados, por lo que es difícil obtener una descripción general rápida ... Pero como dije, probablemente varía mucho del programa programar, incluso para programas más grandes.

Digamos que tiene un solo programa que no usa muchas bibliotecas externas. Luego, puede tener un AuthorizationError específico que ya contiene sus metadatos de usuario y se puede propagar sin cambiar algunas capas sin mucho cambio en las cosas que realmente necesitan manejarlas hasta la capa de solicitud.

No te haces una idea. Las anotaciones son para encontrar fácilmente la forma en que ocurrió el error. De manera similar, tenemos os.NotExist pero esto es apenas una buena pista sobre la ruta de error.

@thomasf aquí hay otro punto de datos, de una copia de trabajo de varios años de juju / juju,

529628 líneas de origen, tryhard encontró 1763 (0,3%) reemplazos.

Si seguro. Dado que ha estado involucrado con ambos, probablemente no sean buenos ejemplos de diferentes formas de escribir programas. No tengo tiempo ni para probar el atm del programa tryhard y mucho menos para ejecutarlo correctamente en diversas fuentes (que podría ser imposible de recopilar de todos modos porque omite el código fuente cerrado si se recopila a través de github)

529628 líneas de origen, tryhard encontró 1763 (0,3%) reemplazos.

Como alguien (cita requerida) dijo sabiamente, try no facilita el manejo de errores. Hace que sea más fácil no manipularlos.

Si analiza el código y encuentra muchos try reemplazos, todo lo que le dice es que el código no hace nada con los errores, excepto devolverlos. Probablemente no sea un buen código. ¿Deberíamos facilitar que la gente sea perezosa y no se preocupe por los errores? ¿No es esa una de las razones por las que Go no tiene excepciones, para evitar precisamente eso?

No voy a tomar posición al respecto. Sin embargo, lo que estoy encontrando es su poca evidencia de apoyo para sugerir que

una. hay muchas ubicaciones donde try es aplicable a las bases de código go existentes
B. el manejo de errores en general constituye una parte significativa de SLOC, según mis propias medidas y los números propuestos por el equipo de Go, ref https://youtu.be/RIvL2ONhFBI?t=440 código de tiempo 07:26

Si analiza el código y encuentra muchos try reemplazos, todo lo que le dice es que el código no hace nada con los errores, excepto devolverlos. Probablemente no sea un buen código. ¿Deberíamos facilitar que la gente sea perezosa y no se preocupe por los errores? ¿No es esa una de las razones por las que Go no tiene excepciones, para evitar precisamente eso?

  1. Estás haciendo juicios de valor sobre un código teórico del que no sabes nada y que no es un buen hábito.
  2. No veo cómo try es más fácil de usar incorrectamente que lo que tenemos ahora, go no tiene funciones para imponer el manejo de errores y ya es muy fácil omitirlo. Para mí, try se trata de hacer que el código sea más fácil de leer SI no necesita manejar errores.

Tal vez no haya necesidad de try porque no será necesario en muchos lugares, pero entretengamos la idea y propongamos casos de uso en lugar de estar de mal humor al respecto ...

No puedo pensar en muchas situaciones prácticas sobre cómo lo usaría, pero aún no existe, por lo que es difícil saber si diseñaría algunas cosas de manera diferente si existiera. Una cosa que me viene a la mente es que podría Lo que se debe hacer es agrupar una serie de acciones en una función anónima como esta usando intentar y aún manejar el error antes de devolverlo a la persona que llama. Podría hacer que algún código sea mucho más legible.

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

Creo que podría ser una buena idea en este momento escribir un sitio web para mantener los datos por tryhard en diferentes paquetes y visualizarlos. Tal vez modificar un poco de golang / gddo (godoc.org) pueda hacer el trabajo.

Prefiero dejar if err != nil solo. Pero si tenemos que agregar algo para el manejo de errores, aquí hay una nueva propuesta que agrega throws palabra clave.

32852

Sin repetir algunos de los argumentos ya expuestos aquí, me hago eco del sentimiento de dejar if err != nil como está.

La perspectiva que puedo ofrecer es la siguiente: como alguien que ha enseñado Go a cientos de recién llegados (tanto a la programación como a Go de otros lenguajes), if err != nil nunca ha sido un problema para ellos. Los programadores experimentados en mis talleres lo encuentran inusual al principio, pero rápidamente aprenden a amar la naturaleza explícita del manejo de errores en Go.

Hay preocupaciones más importantes que podemos abordar en el idioma y la reacción clara de la comunidad a este problema dice que if err != nil no es una de ellas.

Go es perfecto por muchas razones. El principal de ellos es "if err! = Nil". Puede parecer detallado, pero para las personas que aprenden a codificar, facilita la depuración y la corrección del código.

@davecheney

No voy a tomar posición al respecto. Sin embargo, lo que estoy encontrando es su poca evidencia de apoyo para sugerir que

una. hay muchas ubicaciones donde try es aplicable a las bases de código go existentes
B. el manejo de errores en general constituye una parte significativa de SLOC, según mis propias medidas y los números propuestos por el equipo de Go, ref https://youtu.be/RIvL2ONhFBI?t=440 código de tiempo 07:26

Me temo que en el clima actual cualquier ejemplo que encontremos sería descartado como "bueno, probablemente no sea un buen código".

He aquí un ejemplo:

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

Para ser justos, los datos sobre la cantidad de ubicaciones que se encontraron con pruebas pueden confundirse con las convenciones que requieren errores de ajuste. Por ejemplo, si la convención de su empresa es

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

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

que no sería informado por tryhard.

Tenemos una convención de este tipo en mi empresa. Hacer una simple búsqueda y reemplazo para revertirlos a los retornos de error desnudo me da estos resultados:

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

tryhard informa 2736 reemplazos, pero hacer una revisión manual de la envoltura restante parece que se reduce en aproximadamente 1850, por lo que estimaría un total de ~ 4500 try usos en nuestra base de código de 300k líneas.

(Personalmente, estoy a favor de la explicitación actual del manejo de errores y no me importa).

Por ejemplo, si la convención de su empresa es
[envolver errores con un mensaje personalizado]
que no sería informado por tryhard.

Ese es el punto: la propuesta try solo simplifica if err != nil return err devoluciones desnudas, no admite errores de envoltura con un mensaje y contexto personalizados.

La única repetitividad de if err != nil creo que podría arreglarse teniendo que especificar también los valores cero de los otros valores de retorno. El lenguaje podría actualizarse para eludir eso. Por ejemplo:

En el Go de hoy, si tengo una función con esta firma:

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

En algún lugar de la función, tendría que escribir:

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

Obligar al escritor a repetir los mismos valores cero en toda la función.

Sería mucho más fácil (y con poco costo para la verbosidad y la legibilidad del error) si el idioma pudiera completar automáticamente los valores cero para los valores devueltos sin error:

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`)
}

Puedo decir por experiencia con una gran cantidad de código que interactúa con consultas y llamadas a la base de datos, tener que repetir los valores cero en las funciones es el único aspecto negativo del manejo de errores al estilo Go. De lo contrario, estoy de acuerdo con el sentimiento de esta propuesta: ¡Deje if err != nil solo!

Nota: sí, los valores de retorno con nombre pueden _sort of_ lograr esto (https://play.golang.org/p/MLV8Y52HUBY), pero después de implementar algunas funciones en mis propias bases de código usando esta técnica, recordé cuánto de un pie -Los valores de retorno con nombre de arma son; Siempre termino sombreando el valor de retorno nombrado.

Por ejemplo, si la convención de su empresa es
[envolver errores con un mensaje personalizado]
que no sería informado por tryhard.

Ese es el punto: la propuesta try solo simplifica if err != nil return err devoluciones desnudas, no admite errores de envoltura con un mensaje y contexto personalizados.

Eso es cierto, estaba pensando en la variante que permitía agregar una cadena descriptiva. La gran mayoría (~ 4000/4500) de nuestras devoluciones de error son un errgo.Mask(err) sin contexto, que estaba considerando equivalente a un try() sin descripción, pero actualmente sería una reducción en funcionalidad ya que errgo agrega información de pila y la prueba no lo hace (todavía).

@ianlancetaylor hay una propuesta aquí. @miekg propone que usted, como uno de los líderes de nuestro lenguaje, ya no busque el reemplazo de if err != nil con alguna otra construcción que contradiga el espíritu de manejo de errores según lo decidido por los Autores originales de Go. Para mí, personalmente, parece que está tratando de afirmar la poca importancia de esta pregunta moviéndola a golang-nuts lugar de tratarla como nuestras otras propuestas. Puede que esa no sea tu intención, pero es el impacto que siento.

Nuestro método de manejo de errores es único y creo que es un valor agregado masivo sobre otros lenguajes. Cambió por completo mi forma de pensar sobre los errores en los sistemas que construyo y, como resultado, me convertí en un ingeniero de software más sólido. No quiero que complazcamos a la minoría ruidosa, ni a los forasteros, en aras de conseguir más desarrolladores de Go. Creo que deberíamos tomar líneas duras en ciertas cosas, y la forma en que elegimos manejar los errores es una de ellas porque, en última instancia, nos hace mejores al intercambiar la brevedad del código.

Esta es una oportunidad para que el equipo dentro de Google genere más confianza y fe en la comunidad, o para continuar la trayectoria en la que nos encontramos actualmente, que no es buena para el idioma, el ecosistema o sus usuarios.

Pido que el equipo de Go acepte esta propuesta tal como está, mientras continúa buscando otras iteraciones de lenguaje no relacionadas que son un valor agregado más claro.

Es posible que el rastreador no tenga subprocesos, pero personalmente preferiría tener la garantía de que esta propuesta sea respondida de manera oficial y no relegada al grupo de Google, donde puede desvanecerse silenciosamente en la oscuridad.

El tema ya se ha debatido también en el grupo de Google.

La versión actual de # 32437 no es satisfactoria. El comando integrado try () oculta muchas rutas de ejecución para el ojo inexperto. La propuesta original con cheque y manija era muy comprensible y la palabra clave cheque se destacó.

Ahora, el comando integrado try () parece una función; no es obvio que pueda cambiar el flujo de control. También tenemos pánico (), pero está (creo) siempre en una línea propia, tiene un nombre destacado y su uso es escaso. try () por otro lado, podría esconderse dentro de una expresión compleja.

@theckman Robert ha diseñado las primeras versiones de Go with Rob y Ken, y Robert y Russ se han unido al equipo desde el principio. Han estado trabajando en Go desde el principio. Creo que podemos confiar en ellos para saber si una propuesta "contradice el espíritu de manejo de errores según lo decidido por los Autores originales de Go".

No me gusta el principio de una propuesta que congelaría el manejo de errores como lo es hoy. Tal propuesta prohibiría todas las propuestas futuras sobre este tema.

¿Por qué no simplemente aceptar iterar el diseño en su lugar? Teníamos la propuesta de control / control. Pero se discutieron algunos inconvenientes. Esto llevó a la propuesta de prueba. Algunos inconvenientes de esta propuesta se discuten ahora. Quizás esto conduzca a otra propuesta mejor, hasta que se encuentre el enfoque correcto.

Nuestro método de manejo de errores es único

El manejo de errores en Rust es conceptualmente similar a lo que hacemos en Go (los errores son valores, flujo de control explícito, excepto que usamos múltiples valores de retorno cuando usan tipos de suma). Rust tenía el mismo problema que Go con el manejo detallado de errores. ¡Esto llevó a Rust a agregar el intento! macro, y eventualmente el? operador. Yo diría que la comunidad de Rust es incluso más estricta que la comunidad de Go con respecto al manejo de errores (el manejo de errores RFC y las discusiones son esclarecedores). Han encontrado una manera de disminuir la verbosidad del manejo de errores sin la pendiente resbaladiza de un mal manejo de errores. Estoy seguro de que nosotros también podemos.

la trayectoria en la que nos encontramos actualmente que no es buena para el idioma, el ecosistema o sus usuarios

¿De qué estás hablando? Go mejora constantemente. Es increíble tener acceso a un lenguaje, herramientas y documentación tan excelentes de forma gratuita (como en la libertad de expresión).

@theckman Robert ha diseñado las primeras versiones de Go with Rob y Ken, y Robert y Russ se han unido al equipo desde el principio. Han estado trabajando en Go desde el principio. Creo que podemos confiar en ellos para saber si una propuesta "contradice el espíritu de manejo de errores según lo decidido por los Autores originales de Go".

No me gusta el principio de una propuesta que congelaría el manejo de errores como lo es hoy. Tal propuesta prohibiría todas las propuestas futuras sobre este tema.

¿Por qué no simplemente aceptar iterar el diseño en su lugar? Teníamos la propuesta de control / control. Pero se discutieron algunos inconvenientes. Esto llevó a la propuesta de prueba. Algunos inconvenientes de esta propuesta se discuten ahora. Quizás esto conduzca a otra propuesta mejor, hasta que se encuentre el enfoque correcto.

Nuestro método de manejo de errores es único

El manejo de errores en Rust es conceptualmente similar a lo que hacemos en Go (los errores son valores, flujo de control explícito, excepto que usamos múltiples valores de retorno cuando usan tipos de suma). Rust tenía el mismo problema que Go con el manejo detallado de errores. ¡Esto llevó a Rust a agregar el intento! macro, y eventualmente el? operador. Yo diría que la comunidad de Rust es incluso más estricta que la comunidad de Go con respecto al manejo de errores (el manejo de errores RFC y las discusiones son esclarecedores). Han encontrado una manera de disminuir la verbosidad del manejo de errores sin la pendiente resbaladiza de un mal manejo de errores. Estoy seguro de que nosotros también podemos.

la trayectoria en la que nos encontramos actualmente que no es buena para el idioma, el ecosistema o sus usuarios

¿De qué estás hablando? Go mejora constantemente. Es increíble tener acceso a un lenguaje, herramientas y documentación tan excelentes de forma gratuita (como en la libertad de expresión).

La historia del desarrollo de Rust muestra que los tipos detrás de él no tenían idea de lo que estaban haciendo. Básicamente copiaron los principios de procesamiento de errores de Haskell, pero estos no son una buena combinación para la programación imperativa (¿del mundo real?). Su macro ? es solo una solución para el sistema de procesamiento de errores inicialmente fallido.

@ianlancetaylor hay una propuesta aquí. @miekg propone que usted, como uno de los líderes de nuestro lenguaje, ya no busque el reemplazo de if err != nil con alguna otra construcción que contradiga el espíritu de manejo de errores según lo decidido por los Autores originales de Go. Para mí, personalmente, parece que está tratando de afirmar la poca importancia de esta pregunta moviéndola a golang-nuts lugar de tratarla como nuestras otras propuestas. Puede que esa no sea tu intención, pero es el impacto que siento.

Nuestro método de manejo de errores es único y creo que es un valor agregado masivo sobre otros lenguajes. Cambió por completo mi forma de pensar sobre los errores en los sistemas que construyo y, como resultado, me convertí en un ingeniero de software más sólido. No quiero que complazcamos a la minoría ruidosa, ni a los forasteros, en aras de conseguir más desarrolladores de Go. Creo que deberíamos tomar líneas duras en ciertas cosas, y la forma en que elegimos manejar los errores es una de ellas porque, en última instancia, nos hace mejores al intercambiar la brevedad del código.

Esta es una oportunidad para que el equipo dentro de Google genere más confianza y fe en la comunidad, o para continuar la trayectoria en la que nos encontramos actualmente, que no es buena para el idioma, el ecosistema o sus usuarios.

Pido que el equipo de Go acepte esta propuesta tal como está, mientras continúa buscando otras iteraciones de lenguaje no relacionadas que son un valor agregado más claro.

No pueden hacer nada serio con el sistema de tipos actual de los 60. Necesitan finalmente tomar prestadas ideas de los 80 en su Go 2.0

¿De qué estás hablando? Go mejora constantemente. Es increíble tener acceso a un lenguaje, herramientas y documentación tan excelentes de forma gratuita (como en la libertad de expresión).

@ngrilly, esa última parte es probablemente para una discusión más amplia. Sin descarrilar esta propuesta, pero cerrando un poco ese comentario, existe un sentimiento creciente de desalineación entre los usuarios y el liderazgo en la comunidad / ecosistema.

Para el resto de la discusión, no creo que agregar más sobrecarga cognitiva a la sintaxis sea una victoria. Me alegro de que hayan encontrado algo que les funcionó, no somos ellos.

Acabo de abrir una propuesta para la declaración if en línea: https://github.com/golang/go/issues/32860

Referencia: https://github.com/golang/go/issues/32825#issuecomment -506707539

Cuánto mejor sería este mundo si todos los que envían su propuesta sobre cualquier nueva característica de golang 2.0 que les encantaría tener, también proporcionarían una rama de su bifurcación de https://github.com/golang/go (y cualquier otra repositorios necesarios) que implementa esa propuesta.

¿No estás de acuerdo?

@ av86743 Parece más allá del alcance de esta propuesta. Por favor presente una propuesta sugiriendo ese curso de acción.

Veo algún desafío con eso, como el riesgo de mucho esfuerzo desperdiciado antes de que alguien lo rechace basándose en algo en el documento de la propuesta. Luego, pasó todo ese tiempo en una bifurcación que ni siquiera será revisada.

qué tal esta sintaxis:

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

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

@theckman, me disculpo si parece que mi sugerencia de trasladar esta discusión a otro lugar hace que parezca que no es importante. Expliqué mis razones en mi solicitud y creo que aún se mantienen. El equipo de Go considera las discusiones de la lista de correo así como las propuestas.

Ya que mencionas "los autores originales de Go", creo que vale la pena señalar que la propuesta de "probar" fue hecha por @griesemer, que es uno de los tres autores originales de Go.

Estoy totalmente de acuerdo con esta propuesta, creo que lo único que debe cambiarse es simplemente ir a la derecha, hacer que vaya a la derecha para permitir una línea de declaración if.

Realmente quiero una línea de

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

en lugar de tres líneas de

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

@ av86743 Parece más allá del alcance de esta propuesta. Por favor presente una propuesta sugiriendo ese curso de acción.

@theckman Me estás diciendo qué hacer, y esto no solo no es cortés, es grosero en apariencia. Puede intentar posicionarse de la manera que elija, sin embargo, ni yo ni, presumiblemente, nadie más aquí es su mono "ir a buscar" para saltar cuando usted lo diga.

Veo algún desafío con eso, como el riesgo de mucho esfuerzo desperdiciado antes de que alguien lo rechace basándose en algo en el documento de la propuesta. Luego, pasó todo ese tiempo en una bifurcación que ni siquiera será revisada.

Sería sólo un "esfuerzo en vano" para [... _descripción en un lenguaje totalmente apropiado omitido en aras de la brevedad_ ...].

Para un programador, sin embargo, sería un ejercicio trivial pero útil y, al mismo tiempo, un servicio a la comunidad de Go.

@ av86743 Creo que lo que sugirió es una idea interesante y no quería que se perdiera como comentario en un tema no relacionado. Si no tiene interés en llevarlo a cabo a título oficial para su consideración, le pido disculpas por defender que plantee un tema por separado.

Aunque esta propuesta específica proviene de @griesemer , me cuesta creer que haya estado hirviendo de rabia interna durante diez años por la verbosidad de los retornos de error sin envolver en Go. Sin embargo, es un excelente ingeniero y los ingenieros encuentran soluciones a los problemas (percibidos); es muy difícil detenerlos. Nos gusta resolver problemas. El simple olfateo de un problema es suficiente para que empecemos a pensar en todo tipo de ideas. Una vez que ese proceso de pensamiento está bastante avanzado, es difícil para cualquiera de nosotros dejar de lado nuestras supuestas soluciones emocionalmente y considerar que, después de todo, tal vez no sea realmente un problema. Después de todo, hemos tenido un bebé, intelectualmente hablando, y no es fácil simplemente abandonarlo.

Por lo que vale, mi sospecha privada es que el proceso de razonamiento del equipo de Go sobre esto ha sido algo como:

  1. Go es muy popular y ampliamente utilizado, por lo que miles de personas tienen, naturalmente, comentarios, sugerencias y quejas al respecto.
  2. Solo puedes hacer un muro de piedra durante tanto tiempo. El equipo de Go se siente bajo una enorme presión para hacer _algo_ con todo el ruido de la comunidad.
  3. Esto es algo.

¿Qué tal si agregamos una función de captura () en diferir para detectar si en el intento se encontró algún error como recuperar ()?
ejemplo:

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

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

    try doSomthing()
}

en muchas funciones

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()
 }

¿Qué tal si agregamos una función de captura () en diferir para detectar si en el intento se encontró algún error como recuperar ()?
ejemplo:

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

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

    try doSomthing()
}

en muchas funciones

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()
 }

¿Cómo ayuda esto a manejar cada error de una manera separada?

Algunas aclaraciones:

  1. La propuesta try no introduce una nueva sintaxis ni una nueva palabra clave, al contrario de lo que algunas personas han estado afirmando en este hilo. Simplemente introduce una nueva función incorporada, sobre el cambio más mínimo que uno podría hacer para agregar una nueva funcionalidad. Sea preciso al discutir esto, porque es importante. Hay una gran diferencia entre agregar una nueva sintaxis y una nueva palabra clave, y un archivo. El primero es un cambio importante, el segundo una adición relativamente menor. Lo que sugiere la propuesta try es una adición relativamente menor.

  2. Estoy de acuerdo con @ianlancetaylor en que esta discusión se lleva a cabo mejor en otro lugar (golang-nuts). Aquí no hay propuesta.

  3. De hecho, @bitfield , no tengo "rabia interna acerca de la verbosidad de los retornos de error no envueltos en Go" , gracias :-) Pero creo que la verificación de errores es más detallada de lo que quizás sea necesario; y el hecho de que la comunidad haya planteado este mismo sentimiento en repetidas ocasiones es un indicador claro de que nosotros (el equipo de Go) no estamos solos con esta creencia. No iría tan lejos como para decir que hay mucha presión para hacer "algo". Hemos estado trabajando en esto de manera intermitente durante mucho tiempo y estamos bastante contentos de esperar el enfoque "correcto". .

La propuesta try es la solución más mínima que hemos encontrado (con una ayuda significativa de las contribuciones de la comunidad) que aborda el problema de la verificación de errores. La propuesta try es muy explícita sobre el hecho de que _no servirá de nada_ si cada prueba de error requiere manejar el error de alguna manera específica. try solo ayuda cuando todos los errores en una función se prueban y manejan de la misma manera (y luego recomendamos usar defer ), o cuando simplemente se devuelven. Es difícil ser más explícito aquí, pero repitamos lo que dice la propuesta : try no ayudará en todos los escenarios de error. Ayuda en un número significativo de casos. Para todo lo demás, utilice declaraciones if .

@griesemer try es demasiado propenso a errores: no hay RAII en Go, por lo que no podemos dejar la función en muchos casos.

@sirkon , no estoy seguro de qué tan relevante es RAII para esta discusión. try reemplaza los patrones existentes de if ..., err := f(); err != nil { return ..., err } con ... := try(f()) . Si hubo un error de liberación de recursos al usar try , entonces ciertamente también existió de antemano. La introducción de try no mejora ni evita el error de liberación de recursos.

@sirkon , no estoy seguro de qué tan relevante es RAII para esta discusión. try reemplaza los patrones existentes de if ..., err := f(); err != nil { return ..., err } con ... := try(f()) . Si hubo un error de liberación de recursos al usar try , entonces ciertamente también existió de antemano. La introducción de try no mejora ni evita el error de liberación de recursos.

Lea el hilo, hubo un ejemplo:

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

@sirkon He visto ese ejemplo varias veces. Estoy de acuerdo en que es interesante. Pero pensemos un poco más en ello. El builtin try es básicamente azúcar sintáctico para un cierto tipo de texto estándar que se encuentra en el código Go. Entonces podemos convertir ese ejemplo al código original.

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

Ese código tiene el mismo error. Ciertamente he visto un código como ese. No es obvio para mí que el try incorporado hace que ese error sea más fácil de escribir o más difícil de ver.

[Parece que @ianlancetaylor se me adelantó].

@sirkon Este error ya es posible, try o no - Go no le impide escribir código incorrecto. O cambiar esto, usar un código incorrecto como una razón por la cual try no debería permitirse no es un argumento convincente. En cambio, go vet debería marcar los casos problemáticos.

defer es el modismo de Go para limpiar cuando una función regresa, y eso funciona bien. El enfoque correcto aquí, por supuesto, sería:

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

compare esto con:

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

Usando try la fuente se concentra en la preocupación principal: obtener la información del archivo de un archivo. Utilizando el enfoque tradicional, la mayor parte del código fuente se preocupa por posibles errores; y es todo lo mismo. Incluso si queremos decorar el error, el enfoque try funciona a la perfección:

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

usando algo como un paquete errd (vea el problema # 32676).

@griesemer
Mi futuro yo, que hace la revisión del código, sigue gritando que el mecanismo de flujo de control debería estar en su propia línea. ¿Puede este enfoque ser válido (sin aplicación) dentro de la propuesta actual? Además de la legibilidad, se facilita la refactorización a una lógica de manejo de errores más detallada.

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)

Además, este enfoque de handle ve muy bien aquí, pero ¿no se arruinarán los aplazamientos múltiples? ¿O habrá una sincronización con ámbito de función alguna vez (no vi una aclaración en el problema de errd)? Si es así, ¿se otorgaría a las funciones anónimas su propio alcance? Y la sombra ... eesh - ¿quién está primero, qué está en segundo?

Todo esto parece que acabará siendo dos "modos" de escribir código Go. Di que no es así.

@griesemer Si bien tiene razón, el error también es posible hoy en día, creo firmemente que se volverá más frecuente en el futuro con la implementación actual de try. Alguien que provenga de casi cualquier lenguaje popular en el que pueda (). Pensar. De (tiene) encadenamiento (). De (métodos) arraigados en ellos para bien o para mal. Todos estos lenguajes proporcionan sus propios modismos que hacen que los patrones sean naturales y seguros. Llegan a un bloqueo de ruta inmediato con Go, ya que el sistema de tipos los obliga a asignar cada valor con una condición de falla acompañante, simplemente no hay un patrón razonable para evitar esto (o la propuesta de prueba no existiría).

Si se acepta esta propuesta, encontrarán una forma de evitar la placa de caldera if err , permitiéndoles escribir código de una manera familiar para ellos. Excepto que tendrán casi una década de código Go en respuestas de stackoverflow, publicaciones de blog, etc.que se escribieron antes de que se creara el intento. Aprenderán rápidamente a eliminar la declaración err y if con try. Quieren un tamaño de archivo que puedan pegar código envuelto en try hasta que puedan acceder al campo que desean al igual que Stat() en la propuesta de prueba. Es un patrón al que están acostumbrados, por lo que es natural que lo apliquen mientras escriben Go. Dado que Go OS trata todo como un archivo, es justo asumir que se producirán más fugas de recursos.

Lo que me lleva a por qué estoy en total desacuerdo con la afirmación "ya puedes hacer esto hoy", porque simplemente no puedes. Claro, puede filtrar un identificador de archivo. Pero Go no le da a un programador la oportunidad de omitir tener el identificador en el alcance y, por lo tanto, también filtrar el archivo. Cada identificador f omitido es un identificador de archivo filtrado. El uso de la función _requiere_ que se rompan ciertos modismos prominentes en el ecosistema Go. Por lo tanto, la introducción de la función tal como se diseñó hoy aumenta de manera demostrable el riesgo de fugas de recursos en Go.

Dicho esto, como mencioné en https://github.com/golang/go/issues/32825#issuecomment -506882164 En realidad, apoyaría try si se hicieran un par de pequeños ajustes, creo que el cambio sería un bienvenida a la adición al idioma. Todo lo que creo que debe intentar es hacerlo válido solo en el RHS de una asignación y no permitir que el valor de retorno sea direccionable. Haga que los "buenos" ejemplos de uso de try (tienden a ser un try por línea) sean la "única" forma de usar try, es decir:

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")

Creo que esto encajará naturalmente mejor en el lenguaje, mantendrá todos los beneficios actuales de try (aparte de anidarlos, si lo considera un beneficio) sin ninguno de los inconvenientes. No creo que anidar el try le haga ningún favor a nadie, ahorra muy poco, pero brinda posibilidades ilimitadas de abuso. Hoy no me siento particularmente malvado, así que esto es lo mejor que puedo hacer:

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

Pero pensaremos en lo peor, si nos dejas.

@daved Poner try(err) en una segunda línea es totalmente compatible con la propuesta try : try simplemente quiere un argumento que evalúe uno o más valores donde el último valor es de escriba error , que naturalmente se satisface cuando escribe try(err) .

No estoy seguro de seguir su preocupación con respecto a defer - si se requieren diferentes manejadores, defer podría no ser la opción correcta; en su lugar, es posible que se necesite el tradicional if (como se detalla en la propuesta).

@cstockton Estoy de acuerdo en que los try anidados pueden ser muy problemáticos; pero también creo que si tuviéramos try , la mayor parte del código se vería como los ejemplos (válidos)

Por una cuestión de estilo, no hemos puesto restricciones como la que está favoreciendo en el idioma; hemos utilizado go vet para eso. Al final, para el software escrito, el efecto es el mismo. Pero al no tenerlo en el idioma, no nos estamos atando. Es complicado hacer que esas restricciones sean las correctas y hacen que la especificación sea innecesariamente complicada. Simplemente es mucho más fácil ajustar go vet y hacerlo más inteligente a medida que aprendemos más que ajustar el idioma.

@griesemer Gracias por la aclaración. En el ejemplo de código, si la primera línea fuera var err error , ¿el ajuste afectaría potencialmente a ambos errores comprobados? He visto hablar de que el sombreado es una preocupación que puede resolverse en el futuro. ¿Cómo se relaciona / podría eso relacionarse con esto?

¿Qué tal si agregamos una función de captura () en diferir para detectar si en el intento se encontró algún error como recuperar ()?
ejemplo:

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

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

    try doSomthing()
}

en muchas funciones

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()
 }

¿Cómo ayuda esto a manejar cada error de una manera separada?

como otros usuarios comprometidos

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 En estos ejemplos, se asumió que err es el nombre del resultado error . try siempre establecerá esa variable de error de resultado, sin importar el nombre (o la ausencia de un nombre). Si tiene una variable local llamada err entonces esa es una variable diferente. Si desea hacer referencia al error de resultado, deberá tener un nombre diferente. Tenga en cuenta que ya es el caso de que esto no es válido:

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

Por otro lado, si escribe:

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

el err en la asignación es simplemente el mismo que se menciona en la lista de parámetros de resultado. No hay nada diferente aquí de lo que ya fue el caso durante mucho tiempo.

PD: Probablemente deberíamos dejar de secuestrar este tema para las discusiones de try y volver a la propuesta original; se desbloqueará y se abrirá para la discusión mañana (1 de julio) nuevamente.

@godcong Una función catch() (o similar) solo le permitirá obtener el error, no configurarlo (y por lo general, se desea configurar el error de la función adjunta en una función diferida que opera como un controlador de errores) . Se podría hacer que funcione haciendo que catch() devuelva un *error que es la dirección del valor de retorno de error de la función adjunta. Pero, ¿por qué no usar el nombre del resultado del error en lugar de agregar un nuevo mecanismo al lenguaje? Consulte también la propuesta try donde se discute esto.

Además, vea el PS arriba .

@griesemer

Es difícil ser más explícito aquí, pero repitamos lo que dice la propuesta: intentar no ayudará en todos los escenarios de error. Ayuda en un número significativo de casos. Para todo lo demás, use declaraciones if.

Creo que esta es exactamente la falla fatal de la propuesta try() : donde antes había una y solo una forma de hacer la verificación de errores, ahora habrá dos, entremezcladas a lo largo de la base del código. Además, al menos para la base de código en la que estoy trabajando, menos del 20% de if err != nil se puede reemplazar con try() , que aunque no es insignificante, no parece que valga la pena lo suficiente como para crear un dividido en estilos de manejo de errores.

Personalmente, hubiera preferido una construcción de manejo de errores que sea lo suficientemente poderosa como para reemplazar el 95% de todos los casos if err != nil en su lugar. Creo que eso es lo que a mucha gente también le hubiera gustado.

@griesemer Estoy de acuerdo en que la gente aprenderá y las herramientas serán imprescindibles, ya que las guías de estilo, las buenas prácticas, los ejemplos, la documentación, etc., a las que se refiere, estarán desactualizadas. Creo que está claro que intentar, como se propone actualmente, introduce nuevas formas sutiles de escribir software incorrecto. Lo que no está claro es cómo descartar este hecho bajo la premisa de que las personas siempre pueden escribir software incorrecto es un contraargumento válido.

Cambiaré ángulos aquí, ¿cuál es el caso de uso para anidar declaraciones de prueba que sean lo suficientemente fuertes para los posibles efectos secundarios que he descrito? ¿Cómo se beneficia hoy el código Go al permitir que una fiesta entre paréntesis separados por intentos, capaz de anidar en cadena, aparezca salvajemente en cualquier lugar? Supongo que no es así y no creo que nadie haya pedido probar el anidamiento, vino con la propuesta porque está implementado como una función. No desea agregar ninguna restricción como eliminar el anidamiento / ser direccionable para limitar el abuso de anidamiento o los errores sutiles porque haría que la especificación del lenguaje sea más compleja. ¿El tema está aquí para evitar la introducción de complejidad en el lenguaje o para agregar una mejor manera de manejar los errores?

Porque si el objetivo aquí es no hacer que la especificación del lenguaje sea más compleja, la elección es clara: no agregar una nueva función con retornos y parámetros genéricos, es anidable arbitrariamente, proporciona flujo de control y cambia la aridad de los valores que se le dan ( pero solo si satisfacen una interfaz incorporada específica) y probablemente más de lo que estoy olvidando, por ejemplo, una función con una complejidad sin precedentes. Si el objetivo es mejorar el manejo de errores, creo que debería tener que hacerlo sin introducir nuevas formas de producir errores.

@sirkon He visto ese ejemplo varias veces. Estoy de acuerdo en que es interesante. Pero pensemos un poco más en ello. El builtin try es básicamente azúcar sintáctico para un cierto tipo de texto estándar que se encuentra en el código Go. Entonces podemos convertir ese ejemplo al código original.

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

Ese código tiene el mismo error. Ciertamente he visto un código como ese. No es obvio para mí que el try incorporado hace que ese error sea más fácil de escribir o más difícil de ver.

Es obvio para mí que el flujo tradicional "más lento" deja más espacio para notar que el archivo debe cerrarse y este try provoca este tipo de filtraciones ya que la gente tiende a preferir los atajos en lugar de los largos.

@godcong Una función catch() (o similar) solo le permitirá obtener el error, no configurarlo (y por lo general, se desea configurar el error de la función adjunta en una función diferida que opera como un controlador de errores) . Se podría hacer que funcione haciendo que catch() devuelva un *error que es la dirección del valor de retorno de error de la función adjunta. Pero, ¿por qué no usar el nombre del resultado del error en lugar de agregar un nuevo mecanismo al lenguaje? Consulte también la propuesta try donde se discute esto.

Además, vea el PS arriba .

El sistema de tipos de Go se atascó en los años 60 y, por lo tanto, naturalmente no puede manejar bien los casos de borde. Si tuviera la visión de futuro suficiente para tomar prestadas ideas de los 80, tendría métodos para controlar los flujos sutiles propensos a errores. Estás tratando de construir un edificio de vidrio y metal en un pueblo medieval ahora: lo malo es que estos pueblos medievales no tienen tuberías de agua y electricidad, por lo que tú tampoco la tendrás.

Será interesante ver hasta qué punto se emplearán las nuevas y mejoradas instalaciones de error en golang/go . Como mucho.

También será interesante ver si go2 fmt tendrá una opción para generar go1.x formato.

Desde mi propia experiencia, desde que agregué contexto en mi error devuelto por:

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
}

El equipo de soporte rara vez necesita mi opinión para los problemas que surgen en la producción.

En mi humilde opinión, creo que mejorar la gestión de errores no se trata de reducir el código repetitivo, sino de proporcionar una forma más conveniente de agregar contexto a los errores. Todavía no puedo encontrar una buena forma sensata de usar try ().

Tal vez agregar contexto en diferir:

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
}

No parece ahorrar mucha escritura, pero sacrificamos la legibilidad y el rendimiento.

Podría terminar usando try () de esta manera:

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
  }

Reduce algunas líneas, eso es todo.

para mí, la mayoría de las soluciones a este problema parecen romper lo único que pensé que estaba separado de otros lenguajes que usan excepciones: el mecanismo de manejo de errores no se usa como flujo de control. La mayoría de estas soluciones agregan algún tipo de flujo de control (verificar / manejar, probar, capturar, esperar), momento en el que creo que el equipo de Go también podría agregar excepciones y terminarlo.

Aunque me encantaría si pudiéramos tener un caso especial de if y return que se parezca un poco al rubí

return err if err != nil

PD: Perdón por mi inexperiencia en el diseño de idiomas, si he dicho algo estúpido, no dudes en señalarlo y educarme.

¿Qué tal si agregamos una función de captura () en diferir para detectar si en el intento se encontró algún error como recuperar ()?
ejemplo:

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

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

    try doSomthing()
}

en muchas funciones

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()
 }

¿Cómo ayuda esto a manejar cada error de una manera separada?

como otros usuarios comprometidos

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()
 }

En su ejemplo, maneja todos los errores con el mismo diferimiento. ¿Qué sucede si desea agregar un mensaje personalizado e información personalizada al error?

@ianlancetaylor ¿Alguien ha sugerido aumentar el operador ": =" para admitir "retornos inferidos"? Básicamente se comporta exactamente como intentar sin una llamada de función. Esto resolvería muchas preocupaciones que he visto en ambos lados:

  • try como nombre para la función ha sido polémico, siempre y cuando implementemos esto como una función, estaremos estancados dándole un nombre con el que no estoy seguro de que nadie se sienta 100% bien.
  • try hace una cantidad sin precedentes de cosas, tiene entradas como append() y afecta el flujo de control como panic() mientras toma el lugar de un patrón ubicuo ( if err != nil ) .
  • try anidamiento de
  • try se implementa como una función para mantener la compatibilidad con versiones anteriores

Creo que si tuviéramos que simplemente inferir retornos como lo hacemos con los tipos, las cosas se verían concisas y se sentirían más "Go" como:

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

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

_Inserte aquí la descripción correcta de la informática para este comportamiento_, hasta entonces, conformarse con los retornos inferidos a través de declaraciones de variables cortas :

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

Que se ve mucho más ordenado que:

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

Esto resuelve todas las preocupaciones que mencioné anteriormente mientras (en mi opinión) me siento un poco más "Me gusta" y mantiene la compatibilidad con versiones anteriores. También parece un poco más fácil de explicar, el retorno "implícito" en la llamada de función para try() siente realmente fuera de lugar dado el significado omnipresente de try en todos los demás idiomas. No puedo hablar al 100% de la implementación, pero parece que podría hacerse con aproximadamente el mismo esfuerzo. El AssignStmt podría tener un campo agregado que especifique qué expresiones en el LHS omitieron sus valores de error para informar la misma compilación de backend como un builtin?

Me gusta la verificación de errores tal como está, pero si este es realmente un problema que debe resolverse, creo que una nueva palabra clave es probablemente una mejor solución. Es probable que cualquier solución implique cambios en el flujo de control, y es mejor hacerlo lo más obvio posible.

En este ejemplo, la condición on se evalúa cada vez que se establece err .

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

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

    return
}

Esto también funciona sin declarar nombres para los valores de retorno en la firma de la función.

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

    on err != nil {
        return nil, err
    }

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

    return &foo, nil
}

Esto también se puede configurar varias veces. Aquí hay un ejemplo artificial:

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
}

En mi opinión, esto conserva el estilo y la legibilidad de Go, al tiempo que deja en claro lo que está sucediendo en cada paso (una vez que comprende el paradigma), ya que está insertando efectivamente esa condición después de cada ocurrencia de err se establece.

Incluso podrías hacer lo siguiente:

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
}

Finalmente, esto tiene aplicabilidad más allá del manejo de errores. ¿Quieres volver si foo == 0? Otro ejemplo artificial:

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

    x = calcSomeInt(i)
    return true
}

@cstockton

Estaba a punto de protestar que sería demasiado fácil eludir errores de esa manera, pero:

  • Esto ya es un "gotcha" con funciones que devuelven _sólo_ errores, como json.Unmarshal . Los buenos revisores de códigos de IME aprenden a buscar esto con bastante rapidez.
  • Todavía sería un error de sintaxis en funciones que no devuelven valores de error, como http.Handler métodos.
  • Si supiera cómo manejar el error, estaría pensando en él, lo anticiparía y probablemente capturaría el valor del error de todos modos _para que_ pudiera manejarlo.
  • Si su intención era simplemente devolver cualquier error encontrado para convertirlo en un problema de otra persona (el que llama), esto lo logra, con el pequeño inconveniente de que pierde la oportunidad de agregar explícitamente más anotaciones para el contexto.

Entonces, después de sopesar todos los pros en los que puedo pensar y no notar ninguna desventaja obvia, estoy ... en la valla. Sospecho que hay desventajas no obvias que no he considerado. Pero estoy empezando a gustarme hacia dónde va esto, por poco que valga para nadie.

Espero que esta sea la forma correcta de responder y no estoy cometiendo un error.
serio paso en falso.

El 1 de julio de 2019, Chris Stockton [email protected] escribió:

@ianlancetaylor ¿Alguien ha sugerido aumentar el operador ": =" para admitir
"devoluciones inferidas": básicamente se comportan exactamente como intentar sin una función
llama. [...]

Lo que encuentro intrigante en este caso es que tenemos algo
análogo al paradigma "coma OK", donde ahora se omite el "err"
cesionario está permitido en algunas circunstancias bien definidas. Valer
señalando, pero claramente no es suficiente en sí mismo para hacer de esto un
proposición.

Lucio.

Este error ya es posible, inténtelo o no: Go no le impide escribir código incorrecto. O cambiar esto, usar un código incorrecto como una razón por la que no debería permitirse el intento no es un argumento convincente. En cambio, ir al veterinario debería señalar los casos problemáticos.

@griesemer No estoy de acuerdo. Aunque puede ahorrar pulsaciones de teclas, la aritmética de punteros se excluyó de Go con la premisa de que facilita la escritura de código defectuoso y defectuoso. Siento que este es el mismo tipo de característica que hará que los errores sean más difíciles de detectar.

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

El ejemplo típico con try usa ejemplos como el anterior, que para mí obviamente filtra un descriptor de archivo. (El hecho de que Go busque y cierre dichos descriptores en el tiempo de ejecución sin que los usuarios lo sepan en la implementación actual es algo que podría brindarnos comodidad, pero de todos modos hay un mejor ejemplo).

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

Lo anterior se lee como un código correcto, pero ignora el requisito de que el cuerpo de la respuesta se lea en su totalidad y luego se cierre en el camino feliz. Si lo mira el tiempo suficiente, la repugnante elegancia del ejemplo incorrecto debería hacer evidente que veremos este tipo de errores en el futuro.

Como alguien que revisa más que escribe código en este punto, preferiría que Go no agregue características que hacen que la incorrección sea tan tentadora.

@ jesse-amano El uso del operador de asignación en realidad evita que este caso sea posible, sin una declaración de asignación explícita, los siguientes se comportan exactamente como lo hacen hoy, es decir:

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

En cuanto a los valores que solo devuelven un error son elegibles para ser devueltos como return json.Unmarshal(...) y también se pueden representar en la forma más compacta ya que no es necesario que exista un valor fuera del bloque if.

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

Lo que encuentro intrigante en este caso es que tenemos algo análogo al paradigma de "coma OK", donde ahora se permite omitir el cesionario "err" en algunas circunstancias bien definidas. Vale la pena señalarlo, pero claramente no es suficiente en sí mismo para hacer de esta una propuesta válida.

El comportamiento sería idéntico para probar sin parens o encadenamiento y anidamiento arbitrarios. Creo que será difícil encontrar algo que la mayoría de la gente crea que se siente natural sin romper el lenguaje. Lo admito, dado que parece que los autores de Go están bastante decididos a agregar este tipo de función a Go, estoy buscando alternativas porque estoy absolutamente convencido de que try en su forma actual no es una buena opción para Go. Esto es lo más cercano que puedo pensar que no romperá el BC, pero tal vez todavía no se sienta bien para suficientes personas. De cualquier manera, espero que la propuesta de prueba sea denegada o que a alguien se le ocurra algo en lo que mucha más gente pueda estar de acuerdo.

editar: @ jesse-amano Me perdí por completo tu punto, ¡lo siento! Supongo que estar dentro de una función que no devuelve un error mostraría un error típico de compilación de falta de coincidencia en el recuento de asignaciones. Me imagino que el intento probablemente introduciría un nuevo tipo de mensaje de error del compilador para eso.

@beoran Con respecto a https://github.com/golang/go/issues/32825#issuecomment -507126700: El manejo de errores ya es diferente de una situación a otra: a veces devolvemos el error sin cambios, a veces devolvemos un error decorado, a veces actuamos ante un error y, a veces, podemos (correctamente) ignorar un error. Lo único que todos comparten (excepto cuando ignoramos el error) es la prueba err != nil (que ya podemos hacer de más de una forma). Por mucho que sea bueno que una nueva característica del lenguaje capture todos estos casos (o el 95% de ellos), es muy probable que dicha construcción interfiera de manera no ortogonal con otras construcciones de control que ya tenemos. Es decir, lo mejor que podemos esperar es mejorar algunos de estos casos (tal vez el 20%, tal vez el 50% de ellos).

@cstockton Respecto a https://github.com/golang/go/issues/32825#issuecomment -507136111: Si los try anidados son el único obstáculo que queda y go vet no es lo suficientemente bueno , Creo que podemos considerar rechazar try anidados, eso sería bastante fácil. Pero por el momento creo que todavía estamos en la fase FUD (miedo, incertidumbre y duda), sin que prácticamente nadie haya experimentado con try serio. Observo que las personas que lo han hecho, han informado positivamente.

PD: El problema try está abierto nuevamente para recibir comentarios. Sigamos por allá.

@cstockton Oh, absolutamente. Para aclarar, el punto que buscaba era que _ya_es una mala práctica llamar a funciones como json.Unmarshal sin capturar el valor de error en la mayoría de los casos, pero generalmente no se considera una mala práctica para defer file.Close() lugar de defer func() { err = file.Close(); if err != nil { ... }; }() , y aprendimos a diferenciar con bastante facilidad. Así será (probablemente) con su cambio propuesto. Inicialmente me estremecí ante la idea de que alguien usara inocentemente foo := strconv.ParseFloat(bar, 64) cuando tenía la intención de manejar el error, pero después de una breve consideración, realmente no creo que sea un problema después de todo.

@sirkon Con respecto a https://github.com/golang/go/issues/32825#issuecomment -507167388: Deje esas declaraciones claramente sin reservas fuera de la discusión; no tienen lugar aquí. Solo para que conste, muchas ideas en Go son en realidad de los 80 (paquetes, compilación separada, etc.) en lugar de los 60 y muchas son mucho más recientes (goroutines, interfaces). Estas ideas aún pueden parecer viejas, pero han resistido la prueba del tiempo (al menos el poco tiempo que ha existido la CS).

@turtleDev Respecto a https://github.com/golang/go/issues/32825#issuecomment -507231353: Go hace el manejo de excepciones, y se hace con panic y defer y recover - simplemente no lo llamamos "manejo de excepciones" porque ese término viene con una connotación que es engañosa para Go. Pero para que quede claro, Go puede hacer todo lo que puede hacer raise con try-catch , y más (porque en contraste con try-catch , defer se pueden usar condicionalmente). Gracias.

@sorenvonsarvort Con respecto a https://github.com/golang/go/issues/32825#issuecomment -507268293: Si desea una decoración de error diferente para cada caso, use una declaración if . Consulte el documento de diseño. Esta pregunta ya ha sido respondida muchas veces. Gracias.

@cstockton Con respecto a https://github.com/golang/go/issues/32825#issuecomment -507306652: Sí, hemos pensado en tal mecanismo. Específicamente, hemos pensado en "cambiar las tornas" y en lugar de proporcionar try , simplemente proporcionamos handle , que declara un controlador de errores. Si un manejador está presente (y solo entonces), uno simplemente dejaría de lado el err en el LHS de una asignación, y se verificaría implícitamente (como con un try invisible). Se ve bien, pero también es completamente invisible, lo cual es una gran bandera roja. Queremos que el manejo de excepciones sea explícitamente visible en el código, en cualquier lugar. Sin eso, sería casi imposible leer el código y ver dónde está ocurriendo la verificación de errores.

@griesemer gracias por aclarar. pero el pánico y la recuperación tienen diferentes casos de uso y, en su mayor parte, son muy difíciles de encontrar en cualquier base de código de producción. eso lo deja con una cantidad limitada de control de las construcciones de flujo. agregar una nueva construcción rompería esa consistencia, ya que ahora tiene un nuevo control de construcción de flujo que hace algo como retorno.

@ matthew-noken Respecto a https://github.com/golang/go/issues/32825#issuecomment -507323973: estás proponiendo una idea interesante; se parece mucho al mecanismo de observación de un depurador. Hay algunas preguntas que tendrían que ser respondidas: ¿Tiene que regresar el bloque on (sospecho que sí, porque de lo contrario te metes en la tierra del código espagueti)? ¿Se puede tener más de una declaración on ? ¿Qué tan complicada puede ser la condición on (tendrá que evaluarse en cada asignación)? Tenga en cuenta que no podemos tener expresiones arbitrarias porque tenemos que identificar la variable de forma única con la instrucción on . Además, algo anatema en Go: la construcción on implica código invisible que se ejecutará en otro lugar.

Si desea explorar esto más, le sugiero que lo discuta en otro lugar (golang-nuts, o una nueva propuesta diferente). Gracias.

@as respecto a https://github.com/golang/go/issues/32825#issuecomment -507345821:

La aritmética de punteros se excluyó de Go con la premisa de que facilita la escritura de código defectuoso y roto.

En realidad, se excluyó porque habría dificultado o imposibilitado la recolección de basura (y sí, también se puede escribir código inseguro). Pero el punto más importante aquí es que hubo evidencia y experiencia concretas que respaldaron esta decisión.

No hay experiencia ni evidencia todavía de que los try anidados vayan a ser frecuentes o comunes. Pero consulte https://github.com/golang/go/issues/32825#issuecomment -507358397.

@turtleDev Con respecto a https://github.com/golang/go/issues/32825#issuecomment -507368167: Un panic es _exactly_ una excepción, y recover dentro de una función diferida es esencialmente un catch . Pueden ser más difíciles de encontrar en el código de producción porque en Go no recomendamos que escriba su código utilizando excepciones; solo deben utilizarse en circunstancias excepcionales.

En cuanto al número de estructuras de flujo de control: La propuesta es muy clara de que try es simplemente azúcar sintáctico, nada más.

Intenté responder algunos de los comentarios recientes en este hilo en este hilo. Pero continuemos con los nuevos comentarios sobre la propuesta try en el número actual try # 32437 (ahora desbloqueado de nuevo a partir de hoy); y deje este tema reservado para la discusión leave err != nil alone . Gracias.

@cstockton Otro comentario sobre https://github.com/golang/go/issues/32825#issuecomment -507306652: Si implementamos esto, entonces comenzando con

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

y cambiando a

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

significaría que el comportamiento de x := f() repentina y silenciosamente sería muy diferente.

Ejecuté algunos experimentos similares a los que hizo https://gist.github.com/freeformz/55abbe5da61a28ab94dbb662bfc7f763

@ianlancetaylor Creo que esto funcionaría muy bien en la mayoría de los casos y haría que la introducción de mejores informes de errores fuera mucho menos impactante. Considere ejemplos completos para los dos casos principales, primero la persona que llama devuelve un error:

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
}

Creo que, en el caso común, esto es un buen beneficio para este enfoque, los casos de esquina en los que esto crea un problema que creo que ya son frágiles. Solo uno en el que puedo pensar que podría ser realmente desagradable es bloquear un mutex:

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")
   }
   ...
}

Pero creo que el código que está escrito así ya es susceptible de interbloqueos si se basa en una llamada de biblioteca externa para tener éxito y tener un estado de programa válido. Si se almacena en un alcance que puede vivir más allá del pánico, entonces puede bloquearse si la misma biblioteca introduce un pánico en tiempo de ejecución a través de NPE. Además, un motivador principal para escribir código como este es el costo histórico de diferir vivir en el montón. Con la mejora del rendimiento de los diferidos individuales que viven en la pila, dicho código no es realmente necesario. Creo que cualquier derivación de este tipo de error se remedia fácilmente.

Finalmente, como los argumentos de apoyar la deficiencia de "probar" con herramientas también se pueden aplicar aquí. Dada la mayor adopción de los módulos go, tenemos una buena oportunidad de inyectar un paso de "actualización de bibliotecas" para poner tales cambios frente a los usuarios con claridad.

@griesemer

En cuanto a la cantidad de estructuras de flujo de control: La propuesta es muy clara de que intentar es simplemente azúcar sintáctico, nada más.

Disculpe, pero try no va a ser una macro (como C) así que, en efecto, para el usuario final es solo otro control de la declaración de flujo.

Creo que no tengo argumentos objetivos en este momento, así que admitiré que tal vez necesitemos un mejor manejo de errores, pero creo que try puede no ser la mejor solución.

Una vez más, estas son mis opiniones y solo las estoy representando. Estoy seguro de que el equipo de Go ha pensado mucho más en esto que yo.

Lado: Me parece extraño que este problema tenga 1335 votos a favor, mientras que la propuesta try (# 32437) solo tiene 279 votos en contra. Esperaría que las personas que votan a favor de esto rechacen la propuesta try para que los sentimientos de la comunidad al respecto sean más evidentes, porque estas dos propuestas son mutuamente excluyentes.

@griesemer

El manejo de errores ya es diferente de una situación a otra: a veces devolvemos el error sin cambios, a veces devolvemos un error decorado, a veces actuamos sobre un error y, a veces, podemos (correctamente) ignorar un error.

De acuerdo allí, eso es obvio.

Lo único que todos comparten (excepto cuando ignoramos el error) es la prueba err != nil (que ya podemos hacer de más de una forma). Por mucho que sea bueno que una nueva característica del lenguaje capture todos estos casos (o el 95% de ellos), es muy probable que dicha construcción interfiera de manera no ortogonal con otras construcciones de control que ya tenemos. Es decir, lo mejor que podemos esperar es mejorar algunos de estos casos (tal vez el 20%, tal vez el 50% de ellos).

La declaración propuesta try() también "interfiere" con if y return de formas no ortogonales, así que diría que ese argumento no es correcto. A algunas personas no les gusta try() por esa misma razón, pero no estoy de acuerdo. Go no es Oberon, es simple pero no minimalista, Go es más práctico.

En lo que no estamos de acuerdo es en que incluso vale la pena molestarse con una construcción del lenguaje que, como usted mismo admitió, tiene un uso y aplicabilidad limitados, y que ya se puede hacer correctamente con if y return . Creo que muchas personas, como yo, que aprobaron este hilo están tan decepcionadas por try() que preferirían no tenerlo en absoluto. Incluso si no es ortogonal con retorno, un try() más potente y generalmente útil es probablemente lo que a la mayoría de los programadores de Go les gustaría ver.

@beoran ,

Escribiste que te gustaría un try() "más potente" y "más útil en general".

@griesemer mencionó 4 situaciones:

  1. Devuelve el error sin cambios
  2. Devolver un error decorado
  3. Actuar sobre un error
  4. Ignorar un error

try() resuelve 1 por diseño: es literalmente un atajo para if err != nil { return ..., err } .

Las construcciones del lenguaje existente resuelven 3 y 4. Ya podemos actuar sobre un error con if err != nil { ... } y será difícil encontrar una estructura más concisa en ese caso. Ya podemos ignorar un error con _ .

Esto nos deja con 2 (devuelve un error decorado). La propuesta try() sugiere usar una declaración diferir para decorar el error, o si cada error debe decorarse de manera diferente, entonces use una construcción estándar if err != nil { ... } .

El razonamiento está bien explicado en esta parte de la propuesta :

Nuestra primera iteración de esta propuesta se inspiró en dos ideas de Key Parts of Error Handling , que es utilizar una función integrada en lugar de un operador, y una función Go ordinaria para manejar un error en lugar de una nueva construcción del lenguaje del controlador de errores. A diferencia de esa publicación, nuestro controlador de errores tenía la firma de función fija func(error) error para simplificar las cosas. El controlador de errores sería llamado por try en presencia de un error, justo antes de que try devuelva la función adjunta. Aquí hay un ejemplo:

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

Si bien este enfoque permitió la especificación de manejadores de errores eficientes definidos por el usuario, también abrió muchas preguntas que obviamente no tenían respuestas correctas: ¿Qué debería suceder si se proporciona el manejador pero es nulo? ¿Debería try entrar en pánico o tratarlo como un controlador de errores ausente? ¿Qué pasa si el controlador se invoca con un error no nulo y luego devuelve un resultado nulo? ¿Significa esto que el error está "cancelado"? ¿O debería devolver la función adjunta con un error nulo? Tampoco estaba claro si permitir un controlador de errores opcional llevaría a los programadores a ignorar por completo el manejo adecuado de errores. También sería fácil hacer un manejo adecuado de errores en todas partes, pero perder una sola ocurrencia de try . Etcétera.

La siguiente iteración eliminó la capacidad de proporcionar un controlador de errores definido por el usuario a favor de usar defer para el ajuste de errores. Este parecía un mejor enfoque porque hacía que los controladores de errores fueran mucho más visibles en el código. Este paso eliminó todas las preguntas sobre las funciones opcionales como manejadores de errores, pero requirió que los resultados del error fueran nombrados si se necesitaba acceso a ellos (decidimos que esto estaba bien).

[...]

Si determinamos en el futuro que tener alguna forma de función de manejo de errores proporcionada explícitamente, o cualquier otro parámetro adicional para el caso, es una buena idea, es trivialmente posible pasar ese argumento adicional a una llamada de prueba.

¿No está de acuerdo con este razonamiento?

Conozco esta parte de la propuesta y no estoy de acuerdo con ella, porque el hecho de que las preguntas se abrieron debido a la idea de pasar un controlador de errores no significa que no necesitemos tal característica. Simplemente significa que tenemos que pensar bien para responder esas preguntas de una manera razonable.

Además, ahora, manejamos los 4 casos de uso de error con una declaración if err != nil , que es un lenguaje Go consistente y ampliamente entendido. Solo usando try() para el caso 1, y posiblemente para el caso 2, si no nos importa la sobrecarga de hacer un ajuste de error en una declaración defer, significa que el código para el manejo de errores se dividirá en if y try inconsistente, y si el manejo de errores cambia, tendremos que cambiar entre uno y otro.

Una versión más poderosa de try() , que podría usarse en todos los casos, nos permitiría usar try() casi siempre, y esto se convertiría en el nuevo lenguaje consistente de manejo de errores para Go.

Sin embargo, con try() como está ahora, no es lo suficientemente aplicable, y simplemente no se ha ganado suficiente conveniencia para introducir la inconsistencia mencionada anteriormente en el manejo de errores en nuestras bases de código. Es por eso que me siento decepcionado por la propuesta actual try() , y creo que no hacer nada es mejor.

@beoran Creo que los casos 1 y 2 tienen en común que devolver un error de la función (respectivamente sin y con decoración), mientras que los casos 3 y 4 no lo hacen (actúan respectivamente sobre un error e ignoran un error). Creo que el enfoque de 'try ()' está en los casos 1 y 2.

¿Qué pasaría si la propuesta try() pudiera mejorarse para manejar los casos 1 y 2, aceptando una función de controlador opcional? Por supuesto, esto requiere encontrar respuestas razonables a las preguntas enumeradas en la propuesta, pero parece manejable. ¿Apoyarías algo así?

Aquí me Si el caso para esto es que queremos que los usuarios verifiquen los errores, probablemente deberíamos agregar errores comprobados (algo así como excepciones comprobadas). De esa forma somos lo más explícitos posible y el usuario sabe que está comprobando todos los errores.

Con toda seriedad, si yo fuera el que está detrás de tomar estas decisiones, realmente me gustaría reutilizar el operador Kotlin ? o algo así como el comportamiento rust unwrap() .

Cualquiera de estos creo que sería una mejora:

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

donde obtienes nil vuelta si hubo un error en el camino o

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

donde explota a la mitad si hay un error. Rust se ocupa del segundo al tener una función expresiva match que le permite hacer una búsqueda exhaustiva de los errores y manejar cada uno por separado, todo devolviendo un valor de algún tipo en la cadena.

El 2/7/19, Nicolas Grilly [email protected] escribió:

@beoran Creo que los casos 1 y 2 tienen en común devolver un error del
función (respectivamente sin y con decoración), mientras que los casos 3 y 4
no (actuar respectivamente sobre un error e ignorar un error). Yo pienso 'probar ()'
el foco está en los casos 1 y 2.

Estoy algo decepcionado de no ver más discusiones sobre mi sugerencia.
que es la parte de "retorno" del "retorno de error" que debe ser
abordado, no la parte de "error".

Por otra parte, tal vez debería haber seguido un enfoque más oficial. soy
simplemente no soy bueno para formular propuestas, tiendo a ir por todas partes
lugar.

En mi opinión, los casos 1 y 2 están mejor atendidos por un comando de "falla"
palabra clave que indica claramente (después de acostumbrarse) una
cambio en el flujo del programa y que no está sujeto a ninguna
tradición inconveniente en cuanto a su sintaxis completa.

Sin embargo, lo que se sigue de eso, nos guste o no, es que
el posicionamiento de "err" como el último parámetro de retorno pronto será
convertirse en ley en lugar de convención. Ese es uno de los muchos "no intencionados" o
Consecuencias "colaterales" que deben tenerse en cuenta.

Habrá muchas otras consecuencias de este tipo, desde mínimas hasta
desastrosas, algunas que sólo se aprovechan de manera oportunista
propuesta. Yo personalmente pecaría de precaución, lo hago
comprender por qué el equipo de Go y Robert en particular están buscando
apoyo y sin duda están heridos de que la resistencia resultó ser tan
estupendo.

Es un choque de ideologías políticas, quizás necesitemos investigar mucho
más profundo para buscar las raíces reales que necesitan atención de jardinería.

@lootch ¿Tiene una preocupación particular con el error que necesita ser el último parámetro de retorno para que funcione try ? ¿No es eso ya una convención de facto?

(Aparte, no estamos "heridos" por la resistencia, en su mayoría asombrados por la enorme reacción).

@griesemer perdón por la

Solo quiero escribir un comentario rápido diciendo que habiendo escrito mucho de Go (he estado usando Go desde su primer lanzamiento público en 2009 - vea mi github) me agradaría mejorar la ergonomía en torno al manejo de errores. Si bien la claridad del manejo de errores de Go es agradable, también es un problema en el código secuencial. La falta de introspección y de escribir en torno a los valores reales en sí mismos (que se está abordando en una propuesta diferente) no mejora, en mi experiencia, la capacidad de recuperación del programa.

Me enojé lo suficiente con esto hace unos años, de hecho escribí algo de azúcar sobre el pánico y la recuperación para permitir que funcionen (en su mayoría) como excepciones no verificadas: https://hackthology.com/exceptions-for-go-as-a-library. html . En realidad, usar esos contenedores es una mala idea debido al lugar donde se encuentra la comunidad. Sin embargo, sigo creyendo que mejorar la ergonomía en torno al manejo de errores es una victoria.

Parece una locura que la gente esté discutiendo ardientemente por mantener una forma extremadamente detallada pero inútil de propagar las condiciones de error. He escrito y encontrado errores reales en mi código (y en el código de otros) donde la gente ha estropeado la condición if err != nil y ha creado errores. Realmente no hay ninguna razón para que esos errores se escriban alguna vez. La comprobación estática puede ayudar pero no eliminar estos errores.

Apoyo las propuestas para mejorar la ergonomía en torno al manejo de errores.

El proceso de propuesta es, para citar https://golang.org/s/proposal , "Proponer cambios para continuar".
Este problema no propone un cambio, por lo que realmente es un error de categoría.
Si alguien presenta una propuesta titulada "dejar la selección en paz", simplemente la cerraríamos como no una propuesta.

Pero realmente este tema es solo una extensión de la discusión de otros temas como # 32437, así que lo dejaré abierto como un buen lugar para discutir "no hacer nada".

Lo he marcado Propuesta-Retención para indicar que no hay una decisión específica aquí, más bien una metadecisión.

Lado: Me parece extraño que este problema tenga 1335 votos a favor, mientras que la propuesta try (# 32437) tiene _sólo_ 279 votos en contra.

La propuesta de prueba estaba bloqueada cuando me di cuenta de ella, por lo que no pude rechazarla. Supongo que esa es también la razón por la que se introdujo esta propuesta para empezar.

El 2/7/19, Robert Griesemer [email protected] escribió:

@lootch ¿Tiene una preocupación particular con el error que necesita ser el último
devolver el parámetro para que funcione try ? ¿No es eso ya un de-facto?
¿convención?

Lo hago en el sentido de que si eso se convierte en "de facto" grabado en piedra, entonces
esa puerta debe abrirse y hacer que el "error" sea una opción especial y opcional
tipo de argumento y acéptelo, porque para cada resultado correcto hay
son muchas veces incorrectas y también pueden ser
abordado de una manera especial.

Una vez que la idea de que la declaración de "devolución" debe ser investigada
más profundamente (y la propuesta de prueba parece insinuar en ese
dirección), entonces la condición de error ya no es "simplemente un valor, y
El enfoque de Go comienza a parecerse al mejor castillo de naipes construido para
fecha, pero un castillo de naipes, sin embargo.

Soy un usuario reacio de algunos de los trucos más inteligentes de Go (tengo
mencionado en otra parte que x ++ y x-- no aparecen - en su mayoría, me deslizo
a veces, en mi código), así que "intentar" seguirá siendo uno de esos para mí,
pero en principio no me opongo a su introducción, simplemente siento
que tanto se ha dicho, y aún así todas las posibles desventajas pueden no
han sido reveladas (las consecuencias no deseadas de lo que veo convertirse
una eventual decisión política). Poner eso a prueba puede ser bueno
o malo.

Lo que veo es que pánico / recuperación tiene mala reputación y la injusticia
vuelve para perseguirnos. Una vez más, todavía tengo que usar cualquiera de los dos y
Temo el día que tengo que hacerlo porque no soy la galleta más afilada del frasco
y lo encontraré muy difícil, pero ¿se había alentado el pánico / recuperación?
para las raras ocasiones en que es adecuado (ninguno de ellos tratado por
Rob Pike en su maravillosa presentación acerca de que los errores son solo retorno
valores, si mal no recuerdo), sería mucho más claro para todo eso
Go ya maneja los errores tan bien como se puede esperar sin la necesidad
explorar el pantano de posibles opciones disponibles más allá de su
límites.

Dicho esto, tiene mucho sentido poner a prueba, es, después de
todo, una característica opcional. Pero el único efecto secundario es lo que
se trata del intercambio: ¿cuáles serán las consecuencias de "de facto"
funciones de manejo de errores convincentes para tener un argumento de tipo "error"
al final de su lista de parámetros de retorno? ¿Cómo cambiará eso el
percepción de que "los errores son sólo valores"? ¿Cómo encajará con
el ahora mucho más análogo paradigma de "coma-OK"? Que mas sera esto
nuevo principio dar lugar a?

Sobre todo, creo que la montaña que se está haciendo con este grano de arena es
indicativo de que Go ha alcanzado un hito que cambia la vida. Casi
Ciertamente, las suposiciones que eran ciertas en 2013, bien pueden dejar de
sostener. No es que eso sea una novedad para nadie, pero sugiere que Go2 puede
No será tan maravilloso ser compatible con versiones anteriores de Go1 como ha sido
propuesto con demasiada firmeza (en mi opinión).

@lootch Gracias por su respuesta detallada. De hecho, es muy difícil extender un lenguaje existente de una manera compatible con versiones anteriores, por lo que estoy de acuerdo con usted en que si esa compatibilidad con versiones anteriores no fuera necesaria, tal vez uno podría hacer las cosas de manera diferente. Pronto (una vez que los módulos se hayan convertido en un lugar común) podremos realizar cambios de idioma que no requieran una estricta compatibilidad con versiones anteriores.

Como dices, try es un mecanismo opcional y, creo que vale la pena repetirlo, a pesar de todo el alboroto que lo rodea, es un mecanismo muy simple que se explica fácilmente utilizando la funcionalidad Go existente, es solo que podemos ' t escribir try como una función en Go nosotros mismos (y los genéricos tampoco ayudarían). Si pudiéramos, estoy seguro de que sería bastante común ver funciones auxiliares como try para el manejo de errores en el código existente. Después de todo, es exactamente de lo que trata la charla de Rob Pike acerca de que los errores son valores: es programar con errores.

Todavía me sorprende mucho que haya tanto alboroto con respecto a agregar una función de ayuda bastante menor que todos pueden ignorar, sin embargo, la gente está contemplando seriamente cambios significativos en el lenguaje como on error que realmente sugieren un estilo específico de manejo de errores en el idioma.

Gracias de nuevo por tu aportación. Todo esto es muy interesante.

No estoy seguro de si esta propuesta es el lugar adecuado para discutirla, pero dado que ya hay una discusión vívida sobre la palabra clave try en este hilo, la consideraré dentro del tema por ahora :)

Me pregunto si la gente de Google estaría dispuesta a implementar la palabra clave try en su repositorio interno de Golang y, posteriormente, convertir las bases de código de Google existentes para usar esa palabra clave. Al mantenerlo solo interno, permitiría revertirlo fácilmente (es decir, la carga recae en una sola empresa, en lugar de en toda la comunidad).

Esto permitiría hacer un pequeño estudio de caso sobre esta característica en una base de código de alto sloc del mundo real. Facebook ha hecho algo similar con las funciones de PHP en el pasado, y de esa manera pudieron ajustar ciertas funciones antes de proponerlas a la comunidad en general.

Si tuviera que escribir un caso de estudio sobre cómo se usó la palabra clave try _en la práctica_ y qué valor agregó en la vida real, eso podría proporcionar un caso convincente. Si (hipotéticamente) no proporcionaría ningún valor en el mundo real, sería valioso saberlo también. A veces, algo se ve muy bien en papel, pero no funciona en la práctica, o viceversa.

@Freeaqingme Ya hemos producido un CL tentativo que muestra cómo se vería try en la biblioteca estándar: https://go-review.googlesource.com/c/go/+/182717 (puede haber falsos positivos, pero si los hay, son muy raros). Quizás estamos contemplando desarrollar una herramienta que permita la conversión de bases de código en ambas direcciones.

También se le anima a utilizar tryhard en su código base e informar.

Gracias.

@griesemer Puede que no haya sido claro. Supongo que Google usa su propia compilación de Go internamente. Mi sugerencia sería aplicar el parche que vinculó anteriormente a la compilación interna de Google y luego convertir (partes de) la base de código de Google, no solo stdlib, sino especialmente los proyectos internos que están escritos en Go. Si los ingenieros de Google lo usaran durante un par de meses en situaciones de la vida real, esto proporcionaría una buena perspectiva de cómo funcionaría en la práctica.

Obviamente, también puedo aplicarlo yo mismo y usarlo en mis propias bases de código (y también podría hacerlo). Pero solo soy un desarrollador con una base de código relativamente pequeña, por lo que esto no sería tan representativo si muchos empleados de Google lo usaran.

Personalmente, todavía estoy indeciso sobre esta función. Por un lado, aprecio el hecho de que puede ahorrarme un par de pulsaciones cada vez. Pero a veces puedo ser perezoso (después de todo, soy humano) y simplemente anidar tantas declaraciones de prueba como sea posible. Ahora voy a ser un poco más disciplinado, si esta característica estuviera presente. Pero incluso si lo fuera, todavía existe la posibilidad de que las bibliotecas externas abusen / abusen de esta palabra clave mientras mi base de código la sufre (en términos de debugabilidad, extensibilidad de esas bibliotecas).

@Freeaqingme Entendido . Ciertamente podríamos ejecutar try sobre repositorios internos. No estoy seguro de que podamos convertir y volver a convertir, aquí hay un costo significativo involucrado. Además, solo pudimos informar en forma agregada (estadísticas) sobre este experimento, ya que no podríamos informar sobre detalles internos. Esa es la comunidad no tendría una manera fácil de verificar nuestras afirmaciones. Por último, es posible que el código base de Google no sea representativo.

Pero gracias, punto tomado.

@griesemer Aprecio que puede ser un esfuerzo costoso. En cuyo caso no lo haría. Si se trata simplemente de aplicar su proyecto de prueba, el costo podría ser limitado. Pero en realidad eso es algo que un Googler (que yo no soy) debe determinar.

Entiendo que no podría informar sobre proyectos individuales de Google o infraestructura interna. Sin embargo, quizás se puedan compartir algunas anécdotas. Pero lo que personalmente me parecería mucho más valioso es que algunos empleados de Google informen sobre cómo les ha funcionado. Sin entrar en detalles, afirmaciones como "Esperaba X, pero cuando me encontré con casos como A y BI encontré que Y", las encontraría muy valiosas. No encontraría ninguna necesidad de verificar ese tipo de informes.

Por último, es posible que el código base de Google no sea representativo.

Puede que lo sea, puede que no lo sea. Pero hay mucha gente trabajando en Google, así que creo que la mayor parte del código base no se basa en cómo un solo individuo decidió escribirlo, sino que es la culminación de las contribuciones de muchos colaboradores (empleados) diferentes. En ese sentido, espero que las cosas sean lo más representativas posible. Probablemente no exista una base de código 100% representativa.

Mantenlo como está, hasta que encontremos una mejor solución, el try no es el que estábamos buscando. No es necesario que adopte medidas de parcialidad al respecto, tómese su tiempo Go Autores. Creo firmemente, por lo que hablo con los topos de todo el mundo todos los días, que la mayoría de la Comunidad Go no acepta el cajero automático de la propuesta try .

La introducción de una nueva sintaxis significa que todos deben actualizar su versión Go. Todavía estoy usando Go 1.10 porque mi flujo de trabajo se basa en el hecho de que puedo go get cosas y luego usarlas desde la línea de comando (mi GOPATH está en mi PATH ) . Recientemente tuve un problema al intentar go get la biblioteca de otra persona que usa módulos. Recibí un error de que .../v2 no estaba disponible. Esto significa que ya hay una división en el código (piense en Python 2 y 3). Para mí, hay un mundo antes de Go 1.11 y después de Go 1.11. Esto es muy molesto e introducir una nueva sintaxis para algo que funciona tan bien como el manejo de errores en Go no es una buena compensación en absoluto. Introduce más fragmentación.

El 4/7/19, gonutz [email protected] escribió:

La introducción de una nueva sintaxis significa que todos deben actualizar su Go
versión. Todavía estoy usando Go 1.10 porque mi flujo de trabajo se basa en el hecho
que puedo go get cosas y luego usarlas desde la línea de comando (mi
GOPATH está en mi PATH ). Recientemente tuve un problema al intentar go get
la biblioteca de otra persona que usa módulos. Recibí un error de que .../v2 era
No disponible. Esto significa que ya hay una división en el código (piense
Python 2 y 3). Para mí, hay un mundo antes de Go 1.11 y después de Go 1.11.
Esto es muy molesto e introduce una nueva sintaxis para algo que funciona.
así como el manejo de errores en Go no es una buena compensación en absoluto. Eso
introduce más fragmentación.

Si te sirve de consuelo, estoy exactamente en la misma posición frente a
Ir a los módulos. No he encontrado el tiempo y la oportunidad de familiarizarme
con ellos, así que me quedo con Go1.10 también. Quizás eso debería ser
una encuesta que vale la pena tener.

Lucio.

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente o véalo en GitHub:
https://github.com/golang/go/issues/32825#issuecomment -508372318

-
Lucio De Re
Calle Piet Retief 2
Kestell (Este del Estado Libre)
9860 Sudáfrica

Tel .: +27 58653 1433
Móvil: +27 83251 5824
FAX: +27 58653 1435

Soy un nuevo desarrollador de golang (todavía estoy aprendiendo sobre go). Creo que el manejo de errores actual es bueno porque nos permite administrar los errores fácilmente. Como desarrollador de Android, creo que try-catch es más difícil de gestionar nuestro error que if err != nil{ } en golang. Y creo que el manejo de errores explícitos siempre es mejor que el manejo de errores implícitos.

PD. Perdón por mi idioma.

leave it alone

No está roto ...

Amo el meme, @ Daniel1984 :-)

Por cierto, la propuesta try deja solo if err != nil ; simplemente le brinda una opción adicional cuando tiene sentido.

Soy de la opinión de que try no debería incluirse. Sobre la inclusión de try :

Pro

  • Los programadores reducen el número de pulsaciones de teclas que realizan.
  • Los programadores pueden tener una forma abreviada de volver de la función actual a la macro.
  • No es obligatorio.
  • Sería de uso común.
  • Está claro dónde está sucediendo la magia (a diferencia de throws Java).
  • Los ojos ya no se vuelven vidriosos al ver el mar de nil cheques.
  • Funciona mejor con implementaciones simples.

Estafa

  • try agrega un método duplicado para una operación existente.
  • Que try regrese de la función actual es inesperado, también conocido como más magia.
  • Agrega inconsistencias a la verificación de errores.
  • Los programadores sin experiencia en Go no lo entenderán.
  • No cambia el manejo de errores.
  • Menos claridad (desajuste entre el retorno de la función y el valor de la expresión).
  • Es difícil incluso describir con palabras lo que está sucediendo en try .

@griesemer eso es exactamente lo que no me gusta. Las cosas deberían ser simples, no quisiera agregar complejidad al lenguaje solo para tener 2 formas de lograr lo mismo. Hay patrones para evitar esta if err != nil verbosidad. https://www.ardanlabs.com/blog/2019/07/an-open-letter-to-the-go-team-about-try.html

La propuesta de Go2 # 32437 agrega una nueva sintaxis al lenguaje para hacer que el texto estándar if err != nil { return ... } menos engorroso.

Hay varias propuestas alternativas: # 32804 y # 32811 ya que la original no es amada universalmente.

Para lanzar otra alternativa a la mezcla: ¿Por qué no mantenerlo como está ?

Me ha gustado la naturaleza explícita de la construcción if err != nil y, como tal, no entiendo por qué necesitamos una nueva sintaxis para esto. ¿Es realmente tan malo?

Mucho esto. El código explícito es el código correcto. Los horrores que he visto con los manejadores de excepciones son suficientes para alejarme para siempre de esa terrible construcción ilegible.

Parece haber un retraso masivo, y la gente solo comienza a comentar
ahora y uno no pudo evitar tener la impresión de que está fresco
noticias para ellos.

Eso también debe tenerse en cuenta. La vid claramente no es tan
rápido como uno puede pensar.

Lucio.

Y cambie gofmt para permitir declaraciones if de una sola línea que simples hand-ball y error hasta el
función de llamada. Eso eliminaría mucho desorden.

En lugar de:

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

Permitir:

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

Por cierto, la propuesta de prueba deja if err! = Nil solo; simplemente le brinda una opción adicional cuando tiene sentido.

Esta justificación precisa es cómo Go se convierte en otro C ++, C #, Scala, Kotlin, etc. "Bueno, no es necesario que lo uses si no quieres" es la forma en que se crean los lenguajes inflados de características.

Editar: es posible que esto se haya producido de forma incorrecta. No estoy diciendo que try vaya a convertir Go en un lenguaje inflado de funciones. Estoy diciendo que la justificación aquí es un poco defectuosa.

@deanveloper Tiene un claro ejemplo de comportamiento de error de difícil comprensión con "try":
https://github.com/golang/go/issues/32437#issuecomment -498932961

Incluso si es un poco repetitivo, estoy de acuerdo con OP.
Además, todas las alternativas propuestas introducen una complejidad inútil.

Será genial omitir los corchetes cuando solo tenga una línea dentro del alcance

@gonuts

Esto significa que ya hay una división en el código (piense en Python 2 y 3). Para mí, hay un mundo antes de Go 1.11 y después de Go 1.11.

Soy un programador de Python desde hace mucho tiempo y la supuesta "división" que mencionas con respecto a los módulos Go no es nada comparada con el desastre de la migración de Python 2 a Python 3.

Esto es muy molesto e introducir una nueva sintaxis para algo que funciona tan bien como el manejo de errores en Go no es una buena compensación en absoluto. Introduce más fragmentación.

try es una función incorporada en la propuesta. Es totalmente compatible con versiones anteriores. Si su código ya usa try como identificador, entonces su identificador sombreará el try incorporado.

@pongsatornw

Creo que el manejo de errores actual es bueno porque nos permite administrar los errores fácilmente. Como desarrollador de Android, creo que try-catch es más difícil de gestionar nuestro error que si err! = Nil {} en golang. Y creo que el manejo de errores explícitos siempre es mejor que el manejo de errores implícitos.

¿Ha leído la propuesta en su totalidad? try es solo una función incorporada que ayuda a factorizar la repetición de if err != nil { return ..., err } . La lógica general del manejo de errores en Go sigue siendo la misma. Todavía es explícito, los errores siguen siendo valores y no hay try-catch (también conocido como excepciones).

@kroppt

  • try agrega un método duplicado para una operación existente.

try solo está factorizando código repetitivo. Es lo mismo con append . Podemos escribirlo nosotros mismos cada vez que agregamos elementos a un segmento, pero es más fácil llamar a append .

  • Agrega inconsistencias a la verificación de errores.

Puede manipular una porción "manualmente" usando [...:...] o puede usar append , dependiendo de lo que haga. No hay inconsistencia. Son simplemente diferentes herramientas para diferentes tareas. Lo mismo ocurre con los errores, con un if err != nil { ... } simple, o con try , dependiendo de la tarea en cuestión.

  • Que try regrese de la función actual es inesperado, también conocido como más magia.

Es inesperado porque es nuevo. Nos acostumbramos a él a medida que lo usamos más. Y no creo que sea mágico; la especificación del intento es muy sencilla.

  • Los programadores sin experiencia en Go no lo entenderán.
  • Es difícil incluso describir con palabras lo que está sucediendo en try .

Los programadores sin experiencia en Go no entenderán chan , defer , 'go , iota , pánico , recuperar , <- , type assertions, and many other things either without reading the documentation. try` es fácil en comparación con la mayor parte de esto.

  • No cambia el manejo de errores.

Tal vez eso sea algo bueno, según los topos que piden dejar if err != nil solo ;-)

@marcopeereboom

El código explícito es el código correcto. Los horrores que he visto con los manejadores de excepciones son suficientes para alejarme para siempre de esa terrible construcción ilegible.

try tiene absolutamente nada que ver con el manejo de excepciones. ¿Ha leído la propuesta completa? No hay nada comparable al manejo de excepciones como en Java o Python, por ejemplo. try es explícito. Los errores deben mencionarse en las firmas de funciones. Los errores deben manejarse en el sitio de la llamada. No se desenrolla la pila. Etc.

@ gale93

Será genial omitir los corchetes cuando solo tenga una línea dentro del alcance

Creo que la mayoría de los topos tenían el mismo pensamiento y leí propuestas similares muchas veces en el rastreador de problemas. Pero es un cambio mucho mayor que try . No sería razonable hacer esto solo para la instrucción if . Por lo tanto, tendría que cambiar esto en todos los lugares en los que se acepte un bloque. Sin { marque el inicio del bloque, debe especificar una forma de delimitar el final de la expresión condicional. Tienes que actualizar la gramática, el analizador, gofmt, etc. Cambiaría completamente la superficie del lenguaje.

@ngrilly
La moderación y mantener el lenguaje simple es importante.

Algunos de los argumentos que utilizó podrían justificar una gran cantidad de cambios en la especificación. Aquí no solo hay puntos positivos.

Estoy evaluando la decisión en función de si ayudaría o perjudicaría, no necesariamente en la promulgación completa de una determinada filosofía. Tiene razón en que algunas cosas en la especificación violan ciertos principios sobre los que se fundó go , pero se trata de moderación. Este cambio no tiene un impacto lo suficientemente positivo para mí como para tolerar los negativos.

Hola @kroppt ,

mantener el lenguaje simple es importante

Estoy de acuerdo y creo que todos nos esforzamos por lograrlo.

Estoy evaluando la decisión en función de si ayudaría o perjudicaría, no necesariamente en la promulgación completa de una determinada filosofía.

Creo que todos estamos evaluando try función de sus beneficios y costos. La discusión trata de definir y encontrar un consenso basado en hechos sobre esos beneficios y costos, que es lo que traté de hacer en mi comentario anterior.

Tiene razón en que algunas cosas en la especificación violan ciertos principios en los que se fundó go

Durante los últimos años, he estado leyendo casi todo lo que el equipo de Go ha publicado sobre Go y su diseño, y no entiendo a qué te refieres. No creo que la propuesta try viole los principios fundamentales de Go.

@ngrilly
https://talks.golang.org/2012/splash.article describe algunos de los conceptos detrás de lo que hace que go diferente: claridad y simplicidad, entre otros. Creo que este es el conflicto que algunos de nosotros vemos con este nuevo cambio. Es más simple, pero menos claro. A mí me parece que la ganancia en simplicidad es menor que la pérdida de claridad. Quizás estoy equivocado y soy cauteloso.

@kroppt He leído este artículo decenas de veces ;-) No estoy de acuerdo con la idea de que intentar es ofuscar el código. try es solo una función incorporada que se usa para factorizar algún código repetitivo. Hacemos eso todo el tiempo como programadores. Cuando identificamos un patrón repetitivo, lo factorizamos en una nueva función. Si no lo hiciéramos, tendríamos una función main () larga con todo nuestro código insertado en ella.

@ngrilly
Lo que estás describiendo está en mi sección "pro":

  • Los programadores reducen el número de pulsaciones de teclas que realizan.
  • Los programadores pueden tener una forma abreviada de volver de la función actual a la macro.
  • Los ojos ya no se ponen vidriosos al ver el mar de nil cheques.

Una vez más, no veo el sentido de mencionar la aplicación universal de un principio cuando no lo estamos aplicando universalmente aquí.

No estoy de acuerdo con la idea de que intentar está ofuscando el código.

El objetivo del cambio es ofuscar / ocultar / simplificar / representar el código; de lo contrario, veríamos el bloque de verificación de errores original. La pregunta es si deja menos claro el significado.

Creo que go originalmente logró un gran equilibrio de simplicidad, hasta el punto en que contribuyó a la claridad en lugar de quitarla. No puedo explicar cómo lo hicieron, pero try en mi opinión no.

No creo que debamos considerar la verbosidad como un problema. El código debe ser leído y comprendido por humanos, cuyo tiempo es más caro que el de las computadoras, y la _comprensión_ tiende a ser la parte difícil y que requiere más tiempo.

Encuentro que la estructura de sangría del manejo de errores de Go me ayuda a seguir lo que está sucediendo. Cada verificación de errores es explícita. La mayoría de los errores no manejados también son explícitos. Esto hace que el código sea más rápido de entender para mí.

También creo que si bien los cheques if err != nil pueden parecer tediosos, en realidad no necesito _escribirlos_. Solo le pido a mi editor que lo haga.

@kroppt

El objetivo del cambio es ofuscar / ocultar / simplificar / representar el código; de lo contrario, veríamos el bloque de verificación de errores original.

¡Pero podrías usar este argumento para cualquier llamada a función! Si llamo a strings.HasPrefix("foobar", "foo") , ¿ofusca el código? ¿Prefieres escribir y leer l := len("foo"); len("foobar") >= l && s[0:l] == "foo" ?

@rossmcf

Cada verificación de errores es explícita.

try todavía está comprobando explícitamente el error. Es la razón de ser del intento.

También creo que si bien los cheques err! = Nil pueden parecer tediosos, en realidad no necesito escribirlos.

No es tedioso escribir a máquina. Es tedioso de leer, cuando tenemos las mismas 3 líneas en todas partes. Es una repetición, y nosotros, como programadores, solemos descartarlos. Quizás try tiene otros inconvenientes, pero no creo que este.

try todavía está comprobando explícitamente el error
La diferencia entre la abstracción try y strings.HasPrefix es que try devuelve implícitamente.
Al leer el código de go, sé que el flujo de ejecución permanece dentro de mi función hasta que:

  • leer el corchete de cierre de una función sin tipos de retorno
  • lea las palabras clave return , panic
  • leer syscall.Exit(code)
    Con try , no pude leer el código de la misma manera. Escanear visualmente las líneas y ver cero declaraciones de "retorno" ya no significaría "o estas líneas se ejecutan todas, o un bloque, o el programa termina".

@ngrilly Puede responder a más de una persona en una publicación FYI, 10 respuestas en un par de horas con 5 seguidas en un momento hace que sea difícil seguir la discusión. Después de leer sus publicaciones, aparte de algunas falacias, no he visto ningún argumento concreto nuevo que describa los beneficios de intentarlo. He visto un único beneficio: evitar que se escriba if err != nil . Esto tiene el costo de introducir nuevas formas de filtrar recursos , la capacidad de escribir código menos conciso y, lo peor de todo, permite la anidación de intentos .

Siento que la especificación y los argumentos formados por los proponentes son engañosos, actualmente solo proporciona los mejores ejemplos sin mostrar ninguno de los peores ejemplos. No evalúa ni menciona los inconvenientes negativos anteriores ni ningún factor atenuante potencial. No justifica por qué no limita la implementación de try al uso propuesto y demostrado. La herramienta tryhard se está utilizando para mostrar un código más compacto que proporciona subjetivamente una mejor estética a algunas personas, sin una herramienta trytoohard que muestre todas las capacidades de lo que puede hacer, por ejemplo, un intento profundamente anidado. declaraciones. Finalmente, el nombre en sí está asociado de manera ubicua en el mundo de la programación con excepciones, permite que sea encajable y se relacione con errores y lo coloca adyacente como un elemento incorporado a una recuperación y pánico no relacionados que dejan las cosas simplemente fuera de lugar. Creo y confío en la capacidad de los autores de Go para crear algo mejor.

Hay demasiado costo aquí para justificar con un solo valor agregado que veo regurgitado en las respuestas de los proponentes: "Ya no tengo que escribir if err != nil " - lo que se ha defendido fervientemente y se ha arraigado junto con los errores son valores de toda la comunidad de Go. Hemos llegado a una década de código escrito usando if err != nil , que algunos de los avances tecnológicos más notables (docker, k8s, ...) utilizan al mismo tiempo con gran éxito.

Para terminar, if err != nil no es una carga para esconderse con un contenido incorporado, sino algo que todos deberíamos reconocer como un ingrediente fundamental para el éxito de los idiomas. Incluso si aceptamos colectivamente que es una carga, el listón para eliminarlo debe ser alto y sin concesiones. En este momento, demasiados aspectos del intento son un compromiso.

Tengo opiniones sobre qué método es más fácil pero son opiniones. El intento dado es simple y las comprobaciones explícitas actuales son simples. Genial, ambas formas son simples. El problema para mí es que aumenta la carga cognitiva tanto del lector como del escritor de cualquier código dado. Ahora ambos tienen que interpretar múltiples formas de hacer las cosas. Y el escritor tiene que elegir de qué manera hacer las cosas y / o arriesgarse a hacer las cosas de manera diferente al resto del paquete o proyecto en el que está trabajando. Si try reemplazara la verificación explícita, eso aún aumentaría la carga cognitiva debido a los retornos implícitos como otra cosa para analizar.

_Dejando todo eso a un lado por un momento y considerando que ahora tenemos dos formas igualmente simples de manejar los errores, todavía tenemos un problema: _ Lo simple ya no es fácil . Y eso abre la puerta a todo lo que se diseñó para evitar.

La barra para agregar algo como esto debería ser mucho más alta, debería ser experimental durante mucho tiempo para demostrar que es mejor con datos de campo.

@cstockton

Puede responder a más de una persona en una publicación Para su información, 10 respuestas en un par de horas con 5 seguidas en un momento hace que sea difícil seguir la discusión.

Ian sugirió hace 7 días mover esta discusión a golang-nuts exactamente por esta razón (no hay forma de responder a un comentario específico, no hay subprocesos), sugerencia que fue descartada para asegurarse de que la discusión fuera "oficial". Tenemos lo que pedimos.

@therealplato

La diferencia entre la abstracción try y strings.HasPrefix es que try devuelve implícitamente.

Eso es cierto. Al leer una función y buscar puntos de "salida", tendremos que buscar return, panic, log.Panic, os.Salir, log.Fatal, etc ... e intentar. ¿Es ese tal problema? El número de puntos de salida en una función seguirá siendo el mismo y seguirá estando marcado explícitamente, con o sin intento.

leer las palabras clave volver, pánico

el pánico no es una palabra clave; es una función incorporada. Si vamos a criticar la propuesta del equipo de Go, que probablemente sea más competente en diseño de lenguajes que cualquiera de nosotros, al menos deberíamos hacerles el favor de definir correctamente las cosas. 😉

Al leer una función y buscar puntos de "salida", tendremos que buscar return, panic, log.Panic, os.Salir, log.Fatal, etc ... e intentar. ¿Es ese tal problema?

Es un problema, porque try puede aparecer literalmente en cualquier lugar donde pueda aparecer una expresión. Cada punto de salida en Go _sólo_ puede ocurrir como una sola declaración, try es lo único que puede aparecer como una expresión.

@ngrilly

Ian sugirió hace 7 días mover esta discusión a golang-nuts exactamente por esta razón (no hay forma de responder a un comentario específico, no hay hilos), sugerencia que fue descartada para asegurarse de que la discusión fuera "oficial". Tenemos lo que pedimos.

comenzar mensaje

@ usuario1
respuesta 1

@ usuario2
respuesta 2

mensaje final

Esto es lo que se quiso decir.

@cstockton

He visto un único beneficio: evitar que se escriba if err! = Nil.

try evita la escritura y lectura repetitivas de if err != nil { return ..., err } (formateado en 3 líneas), no solo if err != nil .

Esto tiene el costo de introducir nuevas formas de filtrar recursos, la capacidad de escribir código menos conciso y, lo peor de todo, permite la anidación de intentos.

El riesgo de fugas de recursos que mencionó se puede prevenir con vet y lint .

Acerca del "código menos conciso", el objetivo de try es escribir un código más conciso, así que no entiendo tu argumento.

El riesgo de anidamiento excesivo de llamadas a funciones no es específico de try . Cualquier llamada a función puede estar demasiado anidada. Las revisiones de código y el borrado ayudarán, como siempre.

Siento que la especificación y los argumentos formados por los proponentes son engañosos, actualmente solo proporciona los mejores ejemplos sin mostrar ninguno de los peores ejemplos.

Quizás este sentimiento sea mutuo. Todos somos adorables topos; no caigamos en juicios de valor ;-)

Veo regurgitado en las respuestas de los proponentes: "Ya no tengo que escribir si err! = Nil"

Nuevamente, ya no tengo que escribir l := len("foo"); len("foobar") >= l && s[0:l] == "foo" .
Puedo usar strings.HasPrefix("foobar", "foo") lugar.
¿Cómo es tan diferente con try ?
Leí antes que usted aceptaría un try "restringido" que se llamaría check y prohibiría el anidamiento.

Hemos llegado a una década de código escrito usando if err! = Nil, que algunos de los avances tecnológicos más notables (docker, k8s, ...) utilizan al mismo tiempo con gran éxito.

También tenemos mucho código excelente escrito en C, C ++, Java, etc. Con esta línea de razonamiento, no tendríamos Go.

Al leer las discusiones sobre el manejo de errores en Go, no sentí que todos estuvieran en la misma página con respecto a la propuesta try , así que decidí escribir una publicación de blog que demuestre cómo try puede ser utilizado: https://faiface.github.io/post/how-to-use-try/

Discusión asociada en Reddit: https://www.reddit.com/r/golang/comments/c9eo3g/how_to_use_try_faiface_blog/

Sé que este problema está en contra de try , pero espero que mi publicación pueda traer algunas perspectivas nuevas :)

Go está atascado y luchando entre lo mágico o lo lógico para la idea de simple.

Pros:

  • Reducir la repetición
  • Sencillo
  • Patrón existente entre otros idiomas
  • Opcional

Contras:

  • Curva de aprendizaje
  • Magia subyacente
  • Nuevos tipos de errores
  • Go es obstinado y pragmático con if != nil pero podrías usar try

Me siento como esta comunidad especialmente. aquí difiere de las personas que votaron en la encuesta Go [1] .
Es posible que los votantes no elijan esto como su principal preocupación, sino que lo dejen para consideraciones futuras.
Pero se consideró que tenía un impacto debido a su ubicación.

En mi opinión, la función de agregar lenguaje es antigua y la forma de programación moderna es agregar más funciones en los editores, es decir, Emmet o fragmentos de lenguaje, plegado y coloración de código, refactorización y formateo, depuración y prueba, sugiriendo una solución a un error y citando desde godoc o desbordamiento de pila. , UI en la parte superior del código fuente, y deje el código fuente detallado
doblar el código if err != nil en un try

@ngrilly

try evita la escritura y lectura repetitiva de if err! = nil {return ..., err} (formateado en 3 líneas), no solo if err! = nil.

¿Crees que decir "te impide escribir if err! = Nil" significaba que me había olvidado por completo de que también leímos el código que escribimos?

El riesgo de fugas de recursos que mencionaste se puede prevenir con veterinario y pelusa.

Vinculé una discusión por qué siento que el veterinario y la pelusa no es una opción razonable aquí.

Acerca del "código menos conciso", el objetivo principal del intento es escribir un código más conciso, así que no entiendo tu argumento.

Sí, si hubieras leído el enlace al que "la capacidad de escribir código menos conciso " en realidad te apuntaba, es posible que hayas entendido mi argumento. _Note, tomarse un poco de tiempo para comprender los argumentos de las personas con las que está participando en la discusión es el primer paso para presentar información capaz de hacer que cedan a su punto de vista.

El riesgo de un anidamiento excesivo de llamadas a funciones no es específico para intentarlo. Cualquier llamada a función puede estar demasiado anidada. Las revisiones de código y el borrado ayudarán, como siempre.

Interesante, analicemos esto:

1) El riesgo de un anidamiento excesivo de llamadas a funciones no es específico para intentarlo.

Sí, todos aquí comprenden cómo funcionan las funciones.

2) Cualquier llamada a función puede estar demasiado anidada.

Sí, todos aquí comprenden cómo funcionan las funciones.

3) Como siempre, las revisiones de código y el linting ayudarán.

Sí, se expresaba el argumento que ya pelusa y el "argumento de revisiones de código" es otra de control fuera del lenguaje que se hizo en los mensajes que he vinculado a usted .

Quizás este sentimiento sea mutuo. Todos somos adorables topos; no caigamos en juicios de valor ;-)

¿No entiendo? La propuesta no tiene ejemplos de la capacidad completa que proporciona la implementación, solo el uso previsto. La herramienta tryhard utilizada para ayudar a medir los efectos de la propuesta utiliza try en la forma más limitada y razonable, este es un hecho simple.

Nuevamente, ya no tengo que escribir l: = len ("foo"); len ("foobar")> = l && s [0: l] == "foo".
Puedo usar strings.HasPrefix ("foobar", "foo") en su lugar.
¿Cómo es tan diferente con try?

Hago todo lo posible para extraer una posición de cada punto de vista opuesto, de lo contrario no puedo formar un argumento para desmantelarlo. Realmente no puedo ver eso aquí, lo siento. Voy a interpretar esto de la única forma en que puedo encontrarle sentido: literalmente.

¿Cómo es ( strings.HasPrefix ) tan diferente con try ?

strings.HasPrefix

func HasPrefix

func HasPrefix (s, prefijo cadena) bool

HasPrefix prueba si la cadena s comienza con prefijo.

tratar

func try es una nueva función incorporada llamada try with signature (pseudo-código)

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

donde expr representa una expresión de argumento entrante (generalmente una llamada de función) que produce n + 1 valores de resultado de tipos T1, T2, ... Tn, y error para el último valor. Si expr se evalúa como un solo valor (n es 0), ese valor debe ser de tipo error y el intento no devuelve un resultado. Llamar a try con una expresión que no produce un último valor de tipo error conduce a un error en tiempo de compilación.

El try incorporado solo se puede usar dentro de una función con al menos un parámetro de resultado donde el último resultado es de tipo error. Llamar a try en un contexto diferente conduce a un error en tiempo de compilación.

Invocar try con una llamada de función f () como en (pseudocódigo)

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

En otras palabras, si el último valor producido por "expr", de tipo error, es nil, try simplemente devuelve los primeros n valores, con el error nil final eliminado. Si el último valor producido por "expr" no es nulo, la variable de resultado de error de la función adjunta (llamada err en el pseudocódigo anterior, pero puede tener cualquier otro nombre o no tener nombre) se establece en ese valor de error no nulo y devuelve la función adjunta. Si la función adjunta declara otros parámetros de resultado con nombre, esos parámetros de resultado conservan el valor que tengan. Si la función declara otros parámetros de resultado sin nombre, asumen sus correspondientes valores cero (que es lo mismo que mantener el valor que ya tienen).

Si se usa try en una asignación múltiple como en esta ilustración, y se detecta un error no nulo, la asignación (a las variables definidas por el usuario) no se ejecuta y ninguna de las variables en el lado izquierdo de la se cambian las asignaciones. Es decir, try se comporta como una llamada de función: sus resultados solo están disponibles si try regresa al sitio de la llamada real (en lugar de regresar de la función adjunta). Como consecuencia, si las variables del lado izquierdo se denominan parámetros de resultado, el uso de try conducirá a un resultado diferente al del código típico que se encuentra en la actualidad. Por ejemplo, si a, by err son todos parámetros de resultado con nombre de la función adjunta, este código

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

siempre establecerá a, b y err, independientemente de si f () devolvió un error o no. A diferencia de

a, b = try(f())

dejará ayb sin cambios en caso de error. Si bien esta es una diferencia sutil, creemos que casos como estos son raros. Si se espera un comportamiento actual, mantenga la instrucción if.

Son diferentes en que el texto completo que se encuentra en la descripción de try no está presente en strings.HasPrefix. Una mejor pregunta sería en qué se parecen, a lo que les respondería que ambos comparten algunos aspectos de las Convocatorias y nada más.

Leí antes que aceptaría un intento "restringido" que se llamaría cheque y prohibiría el anidamiento.

Me alegro de que leas mi argumento central en contra de try: la implementación no es lo suficientemente restrictiva. Creo que la implementación debe coincidir con todos los ejemplos de uso de propuestas que sean concisos y fáciles de leer.

_O_ la propuesta debe contener ejemplos que coincidan con la implementación para que todas las personas que la consideren puedan estar expuestas a lo que inevitablemente aparecerá en el código de Go. Junto con todos los casos de esquina a los que podemos enfrentarnos al solucionar problemas de software menos que idealmente escrito, lo que ocurre en cualquier idioma / entorno. Debería responder preguntas como cómo se verán los seguimientos de pila con múltiples niveles de anidamiento, ¿son fácilmente reconocibles las ubicaciones de los errores? ¿Qué pasa con los valores de método, las funciones literales anónimas? ¿Qué tipo de seguimiento de pila produce lo siguiente si falla la línea que contiene las llamadas a 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)))

Soy muy consciente de que se escribirán muchos códigos razonables, pero ahora estamos proporcionando una herramienta que nunca ha existido antes: la capacidad de escribir código potencialmente sin un flujo de control claro. Así que quiero justificar por qué lo permitimos en primer lugar, nunca quiero perder el tiempo depurando este tipo de código. Porque sé que lo haré, la experiencia me ha enseñado que alguien lo hará si tú lo permites. Ese alguien es a menudo un yo desinformado.

Go proporciona las menores formas posibles para que otros desarrolladores y yo perdamos el tiempo unos a otros al limitarnos a usar las mismas construcciones mundanas. No quiero perder eso sin un beneficio abrumador. No creo que "porque el intento se implementa como una función" sea un beneficio abrumador. ¿Puede darnos una razón por la que lo es?

No pierda tiempo en este problema de manejo de errores, denos genéricos y crearemos algo como el resultado de Rust.

Go está atascado y luchando entre lo mágico o lo lógico para la idea de simple.

Pros:

  • Reducir la repetición
  • Sencillo
  • Patrón existente entre otros idiomas
  • Opcional

Contras:

  • Curva de aprendizaje
  • Magia subyacente
  • Nuevos tipos de errores
  • Go es obstinado y pragmático con if != nil pero podrías usar try

Me siento como esta comunidad especialmente. aquí difiere de las personas que votaron en la encuesta Go [1] .
Es posible que los votantes no elijan esto como su principal preocupación, sino que lo dejen para consideraciones futuras.
Pero se consideró que tenía un impacto debido a su ubicación.

En mi opinión, la función de agregar lenguaje es antigua y la forma de programación moderna es agregar más funciones en los editores, es decir, Emmet o fragmentos de lenguaje, plegado y coloración de código, refactorización y formateo, depuración y prueba, sugiriendo una solución a un error y citando desde godoc o desbordamiento de pila. , UI en la parte superior del código fuente, y deje el código fuente detallado
doblar el código if err != nil en un try

Yo fui uno de los que votó por un manejo de errores más estricto sin la posibilidad de olvidar procesar un error. No es para intentarlo.

Necesitaremos mucho más que genéricos para rehacer cualquier cosa remotamente como el tipo Result Rust. Incluso _si_ el tipo Result pudiera hacerse únicamente con genéricos, los programadores principiantes necesitarían saber los genéricos antes de poder manejar un error correctamente, o devolver un error de una función "de la manera Result "

@deanveloper , mi punto es: tengo mucho más para beneficiarme de los genéricos que del "cambio de sintaxis" y creo que también es cierto para la comunidad.

@txgruppi Estoy de acuerdo en que los genéricos deberían tener mayor prioridad. Solo estaba tratando de decir que no creo que los genéricos sean un buen sustituto para el manejo de errores.

@deanveloper , en mi opinión, este problema de manejo de errores es solo cosmético, la gente está pasando tiempo discutiendo algo que es estable y funciona bien solo porque tiene que escribir algunos códigos adicionales. Simplemente aprenda a escribir un código mejor y resuelva esto con cambios de diseño.
Antes de que alguien diga que Genéricos es tan simple de arreglar con un mejor código: errores de tiempo de compilación ...

¿Se puede resolver con un fragmento o una macro de teclado? entonces no es un problema.

@txgruppi

Simplemente aprenda a escribir un código mejor y resuelva esto con cambios de diseño.

El 70% de todo el código de manejo de errores en la biblioteca estándar es actualmente adecuado para try como descubrió Robert Griesemer fmt.HandleErrorf (aún no existente). Espero que no quiera llamar a la biblioteca estándar un código incorrecto.

¿Se puede resolver con un fragmento o una macro de teclado? entonces no es un problema.

También se trata de leer el código. Por eso no nos gusta thing.Thing thing = new thing.Thing(thing.THING);

@faiface , ¿'if err! = nil' se está poniendo en camino de desarrollar software de calidad? No lo creo.
¿La falta de genéricos se interpone en el camino del desarrollo de software de calidad? Sí lo es.

La forma en que lo veo es: no tengo suficiente conocimiento para implementar genéricos, así que necesito que alguien lo implemente, pero esta cosa de manejo de errores es solo una pérdida de tiempo para aquellas mentes que pueden hacer que los genéricos sean una realidad. No estoy en contra de este manejo de errores porque sea algo malo, estoy en contra porque hay cosas más importantes que resolver.

@faiface, la biblioteca estándar no es una buena representación del código Go real. Esto se debe a que es mucho más probable que la biblioteca estándar simplemente pase por alto los errores sin agregar contexto, por ejemplo, io/ioutil nunca necesita realmente decorar errores, simplemente puede pasar por alto el error que ocurrió en io . Robert Griesemer también admitió que stdlib no es exactamente el mejor representante del código Go de la vida real, sin embargo, estoy en un dispositivo móvil en este momento y no quiero buscar el comentario. Sin embargo, estoy bastante seguro de que estaba relativamente cerca de su publicación de prueba original.

@deanveloper @faiface Cuando se corre contra el 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)

Por lo tanto, en el código de la vida real, el 40% de las sentencias if están escritas para la verificación de errores, y try puede eliminar el 59% de ellas de forma inmediata.

Estoy de acuerdo. Estoy bien con if err! = Nil. Es simple y limpio para funciones que devuelven valores de error únicos. También me gusta el paquete de errores y sus funciones de causa / envoltura cuando el contexto del error es importante. El uso de errores personalizados con una propiedad de código (que yo sepa) requiere que haga una afirmación de tipo o use algo en lugar de la interfaz de error estándar por completo. De cualquier manera, nunca me he encontrado leyendo o escribiendo código Go y sintiendo algún tipo de molestia con la forma en que funciona actualmente el manejo de errores. Las molestias con las que me he encontrado son casos en los que pueden ocurrir múltiples errores como resultado del procesamiento de una colección de elementos. Sin embargo, este es un problema de diseño y no un problema de idioma.

Tenga en cuenta que cuando digo "cuando el contexto del error es importante" me refiero a una situación en la que tal vez se produjo un error en la red y, por lo tanto, quiero volver a intentarlo, o tal vez los resultados de una llamada de tipo "buscar" no arrojaron resultados porque hubo en realidad, ninguno, o mi dedo nervioso agregó una 's' aleatoria a mi consulta SQL, lo que la está haciendo explotar (esto me sucede a menudo ... probablemente debería hacerme un chequeo por daño en los nervios).

El 5/7/19, Nicolas Grilly [email protected] escribió:

@kroppt He leído decenas de veces ;-) No estoy de acuerdo con la idea de intentarlo
ofuscar el código. try es solo una función incorporada que se usa para factorizar algunos
código repetitivo. Hacemos eso todo el tiempo como programadores. Cuando identificamos un
patrón repetitivo, lo factorizamos en una nueva función. Si no lo hicimos, nosotros
tendría una función main () larga con todo nuestro código insertado en ella.

Me siento tentado a llamarte falso, pero respeto el de Ian Lance Taylor.
esfuerzos sobrehumanos para mantener la discusión cortés y realmente no puedo
vea lo que cualquiera ganaría mintiendo intencionalmente en este foro.

Dicho esto, "cuando identificamos un patrón repetitivo, lo factorizamos en
una nueva función ". Claro, pero no proporcionando una construcción conflictiva
que, en esta última etapa del desarrollo de Go, viene con dos
características ocultas: la primera es el tratamiento de funciones cuyo "retorno
la lista de argumentos termina en un error valor "como especial (o todo lo demás
como un error semántico) y el segundo proporciona un flujo de control oculto
desvío que es análogo pero no del todo idéntico a un "retorno"
declaración.

No importa los aros encendidos que el uso de "diferir" introduce para tratar
con usos más arcanos de "prueba - la pseudo función". Alguien
en otra parte dice, aproximadamente "No quiero encontrar try en el código
Leo ". Siento lo mismo y eso no debería ser barrido bajo el
alfombra.

He dicho que es el aspecto de "devolución" de "devolución de error" lo que
debe tratarse, y la propuesta "on err" se acerca más a
ese principio, pero también dobla un poco las reglas. El mío también
sugerencia de "falla" (mueve el último argumento para que sea el primero, eso es
haciéndome infeliz).

Más profundamente, lo que ningún lenguaje con la posible excepción de SNOBOL,
que conozco ha dado el salto que Rob Pike describió como
"los errores son valores" en la medida en que Go tiene, pero algo se perdió
en el proceso: una "condición" de error "no" es un valor. Exitoso
la finalización de una función es un caso especial y también lo es cada posible
falla.

Cada uno (y eso se aplica a la finalización con éxito, de los cuales puede haber
más de uno, también) necesita ser tratado por sus méritos, pero nosotros
Insistir en que la función invocada debe decirnos su opinión sobre
la calidad de finalización en forma abreviada, algo que tiene
se ha hecho para siempre y Rob ha demostrado ser un error.

Para ilustrar el punto, considere los valores de retorno de un lector:
io.EOF es un caso especial que a veces es un éxito y a veces un
falla, pero según los estándares de Go, se enorgullece de ser un error ("io.Err! = nil").
¿Vamos a tener alguna forma de abreviar eso también? Casi seguro
no, porque estamos bastante acostumbrados a "perdonar" su "maldad".

He esperado mucho tiempo a que una salida de bucle transmita un "estado" similar o
código de condición (una búsqueda puede terminar con un valor encontrado o un error, cómo
¿Dices cuál, si deseas hacer cosas diferentes? Agrega una prueba,
donde el conocimiento ya está en su lugar - mismo problema, diferente
contexto).

Estas son mejoras reales a los lenguajes tradicionales: reducción de caldera
El plato es absurdo, en comparación.

Y, la comparación con el operador ternario es igualmente válida: si?:
no está permitido "para que no se abuse de él", entonces no se debe permitir el intento,
tampoco, al menos por esos motivos.

Francamente, intentarlo es una broma. Hace que Go sea más atractivo para el mal
audiencia. Además del peligro de hacer eso, ¿quién quiere a las personas equivocadas?
para unirse a nuestra comunidad? - está la cuestión de los precedentes y la cuestión
de consecuencias no deseadas. Yo diría "deséchelo" y aceptemos eso
todavía no hemos llegado a un punto en el que la compatibilidad con versiones anteriores pueda
burlarse, por lo que el manejo de errores debe permanecer detallado.

Tenemos pánico / recuperación y hay que promoverlo desde la tercera categoría.
ciudadano al ayudante dispuesto que pueda ser, con todas las explicaciones
que hacen a los novatos (y a mí, admito que le tengo miedo) más
seguro de usarlo.

La construcción "diferir" dentro del manejo de errores (que he adoptado -
sin darse cuenta de su importancia en otros lugares, consistentemente en la finalización
Transacciones SQL: tx.Rollback / tx.Commit) ha sido una revelación para mí.
Puede haber más principios que se puedan aprender "dentro" del alcance de
lo que Go ya ofrece: permanezcamos dentro de esa casilla por ahora.

Una de esas cosas, improvisada, sería pasar a un informe de errores
función una lista de "métodos de error" para ser ejecutados bajo convencional
condiciones (io.EOF, sql.ErrNoRows), en lugar de informar el resultado
como en blanco y negro. Pero no tengo educación en tales asuntos, mis sugerencias
eres demasiado ingenuo, deja que los demás (Roger, ¿estás escuchando?)
ideas a buen término.

Lucio.

"No es tedioso escribir a máquina. Es tedioso leer, cuando tenemos el mismo
3 líneas por todas partes. Es una repetición, y nosotros, como programadores, normalmente
tienden a factorizarlos. Tal vez el intento tenga otros inconvenientes, pero no este
uno, creo ".

Una vez más, falso o, como mínimo, racionalizador. Concedo que esos
tres líneas se leen muchas más veces de las que se escriben, pero el
El dolor que Griesemer pretendía aliviar está en la escritura, no en la lectura.

  • que sin duda él percibe como una consecuencia beneficiosa.

Pero "err! = Nil" es un marcador muy familiar y cuando "lee", como
opuesto a "buscar" con un editor, ese patrón es fácil de
Spot y fácil de cancelar. Factorizarlo no es lo mismo
liga en absoluto. Y el precio está mal.

"Nosotros, como programadores", tendemos a descartar primero los patrones más complejos,
incluso si ocurren raramente. También sabemos que "if err! = Nil {return
err} "se compila en una secuencia de instrucciones muy simple, si
nadie no lo hace, que levanten la mano aquí. ¿Puede uno ser igualmente
¿Confía en que sucederá con "probar la función"?

Lucio.

@lootch Oye, es realmente improductivo llamar a la gente falsa, estoy bastante seguro de que no lo son, ya que las declaraciones que marcaste como tales son bastante razonables.

Como programadores, descartamos patrones repetitivos, eso es absolutamente cierto.

Una gran cantidad de código repetitivo ralentiza la lectura, por lo que tampoco veo cómo eso es falso.

Tus contraargumentos a estos son básicamente "vamos hombre, no es tan gran cosa". Bueno, para mucha gente lo es.

¿Quién quiere que las personas equivocadas se unan a nuestra comunidad?

Esta es una puerta de entrada súper arrogante. Además, la herramienta tryhard ha revelado que try es directamente aplicable en gran parte de las bases de código Go actuales. Se puede aplicar directamente al 70% del código de manejo de errores en la biblioteca estándar. Con cambios en el código (para usar la decoración de errores usando defer , etc.), creo que será aplicable a más del 80% del manejo de errores en todo el código Go.

De acuerdo, estoy sobrepasando la marca, aquí. Pido disculpas.

Supongo que algunos de nosotros nos estamos poniendo calientes mientras esta discusión
está dando vueltas en círculos.

El 7/7/19, Michal Štrba [email protected] escribió:

@lootch Oye, es realmente improductivo llamar a la gente falsa, soy
bastante seguro de que no lo son, ya que las declaraciones que marcó como tales son bonitas
razonable.

Como programadores, descartamos los patrones repetitivos, eso es absolutamente
verdadero.

Una gran cantidad de código repetitivo ralentiza la lectura, por lo que no veo cómo es
falso tampoco.

Tus contraargumentos a estos son básicamente "vamos hombre, no es tan
gran cosa ". Bueno, para muchas personas lo es.

¿Quién quiere que las personas equivocadas se unan a nuestra comunidad?

Esta es una puerta de entrada súper arrogante. Además, la herramienta tryhard ha revelado que
try se aplica directamente en gran parte de las bases de código Go actuales. Puede ser
aplicado directamente al 70% del código de manejo de errores en la biblioteca estándar. Con
cambios en el código (para usar la decoración de errores usando defer , etc.), creo
será aplicable a más del 80% del manejo de errores en todo el código Go.

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente o véalo en GitHub:
https://github.com/golang/go/issues/32825#issuecomment -508971768

-
Lucio De Re
Calle Piet Retief 2
Kestell (Este del Estado Libre)
9860 Sudáfrica

Tel .: +27 58653 1433
Móvil: +27 83251 5824
FAX: +27 58653 1435

@lootch ¡ Apoyos para ser consciente de sí mismo! Puedo entender la frustración de ver la discusión Ir en círculos.

Yo también lo veo de manera similar y estoy del otro lado.

Quizás ambas partes simplemente no se entienden. ¿Has leído la publicación de mi blog llamada Cómo usar "probar"? donde intento mostrar cómo se vería el uso de 'intentar' en la práctica, haciendo todo lo posible para mantenerme imparcial?

El 7/7/19, Michal Štrba [email protected] escribió:

[...]
Quizás ambos lados simplemente no entienden al otro lado. Tienes
lea la publicación de mi blog llamada Cómo usar 'tratar'? donde intento
mostrar cómo se vería el uso de 'intentar' en la práctica, haciendo todo lo posible para quedarme
¿imparcial?

Confieso que no, deseo fervientemente no tener que hacerlo nunca :-)

¿Ha considerado los aspectos que creo que planteó Stockton, donde solo
se muestran las ventajas del intento y pide que el vientre suave
ser revelado también? Me temo que estoy de acuerdo con él y, sin intención de ofenderlo,
que tu blog puede sufrir las mismas deficiencias.

Si no es así, por favor dígame, un buen material de lectura tiene un lugar especial.
en mi vida :-)

Lucio.

@lootch Hice todo lo posible para mostrar tantos aspectos de 'probar' como fuera posible (es decir, tanto cuando se aplica como cuando no) en una pequeña cantidad de código y hacerlo lo más imparcial y realista posible. Pero, por supuesto, no confíe en mi palabra :)

Este fue el comentario más votado en la discusión de Reddit asociada :

Este es un ejemplo hipotético imparcial útil. Gracias por agregar algo constructivo a la conversación que no solo "apesta".

@lootch Hice todo lo posible para mostrar tantos aspectos de 'probar' como fuera posible (es decir, tanto cuando se aplica como cuando no) en una pequeña cantidad de código y hacerlo lo más imparcial y realista posible. Pero, por supuesto, no confíe en mi palabra :)

Este fue el comentario más votado en la discusión de Reddit asociada :

Este es un ejemplo hipotético imparcial útil. Gracias por agregar algo constructivo a la conversación que no solo "apesta".

¿Función con la ruta del archivo como argumento? Esto solo sería una razón por la que este código no pasaría mi revisión. ¿Qué pasa si faltan algunos campos? ¿Reordenado?

@sirkon Para no ser demasiado extenso, el ejemplo está simplificado, por supuesto. Sin embargo, los cambios que serían necesarios para solucionar los problemas que planteó no afectan las prácticas de manejo de errores, que es todo lo que importa allí.

Los cambios que serían necesarios para solucionar los problemas que planteó no afectan las prácticas de manejo de errores

¿Porque lo dijiste?

  1. Empiece por el título de su blog: debería llamarse "cómo no escribir", porque, repito, usar la ruta del archivo como parámetro es una práctica realmente mala y, francamente, un código completo a continuación también.
  2. Te das cuenta de esto
    go resp := Respondent{ Name: name, Gender: try(parseField(s, &line, "gender")), OS: try(parseField(s, &line, "os")), Lang: try(parseField(s, &line, "lang")), }
    producirá mensajes de error deficientes? Debe haber un mensaje de error de campo inesperado y al menos un mensaje de error de campos faltantes. El diagnóstico de su guión es deficiente.

PD Eché un vistazo a tus repositorios. ¿Te das cuenta de que Go es una mala herramienta para tus tareas? Debe comprender que en la práctica de aplicaciones de Go real, los primeros que verán los registros serán los ingenieros de operaciones, no los desarrolladores. Un mensaje de error adecuado puede ayudarlos a resolver un problema por sí mismos.

@sirkon Vamos, no hagas flamewars.

¿Se da cuenta de que esto producirá mensajes de error deficientes?

Son totalmente adecuados al modelo. Se espera que el formato contenga todos los campos y en orden. El mensaje de error lo dice muy claramente.

Si desea cuestionar la calidad del código, ¿por qué no lo reescribe según sus estándares de calidad? Si lo hace, intentaré hacer todo lo posible para reescribir su código para usar try .

PD Eché un vistazo a tus repositorios. ¿Te das cuenta de que Go es una mala herramienta para tus tareas?

¿Sugieres otro para mis tareas? He usado bastantes. Por cierto, esto está bastante fuera de tema.

@faiface

¿Sugieres otro para mis tareas? He usado bastantes. Por cierto, esto está bastante fuera de tema.

¿Oxido? C ++?

@sirkon

¿Oxido? C ++?

Aquí vamos. He usado ambos antes de conformarme con Go. Nunca miré atrás.

@sirkon Uno de los grandes defectos de try es que desalienta la decoración de errores. En este caso, el programador estaba mostrando posibles aplicaciones de try , por lo que, por supuesto, no habrá mucha decoración de errores.

Además, desacreditar a las personas en función de los proyectos en los que han trabajado está completamente fuera de tema y fuera de lugar. Has sido bastante grosero con tus últimos comentarios, y quiero que al menos seas consciente de eso.

@deanveloper ¡ Gracias por el comentario!

Por cierto

En este caso, el programador estaba mostrando posibles aplicaciones de try, por lo que, por supuesto, no habrá mucha decoración de errores.

En caso de que te refieras a mi blog, en realidad hay una gran cantidad de decoración de errores, pero no exactamente de la misma manera que @sirkon lo haría. Aquí hay algunos mensajes de error del programa que usa 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 Mi error, debería haber sido más específico. try desalienta la decoración de errores para cuando desee varios mensajes de error dentro de la misma función. Era posible hacer esto con check/handle draft y con las contrapropuestas del "manejador nombrado". Hubiera sido muy útil en la instancia específica señalada (donde estaba usando try mientras inicializaba una estructura) poder agregar decoración alrededor de cada mensaje, pero desafortunadamente la propuesta de prueba hace que sea un poco difícil de hacer sin escribir tu propia función.

Sin embargo, comprobar / manejar no habría ayudado tanto en su caso específico. Pero la idea propuesta de catch y otras contrapropuestas a try habrían podido manejar los errores como agregar decoración adicional.

@deanveloper Bueno, la mayoría de las veces necesita usar la misma decoración para todos los errores dentro de una función porque se espera que las subfunciones proporcionen su propio contexto. Sin embargo, cuando tenga que decorar las cosas de manera diferente en una sola función, todavía hay una solución fácil con probar:

..., err := functionThatCanFail(...)
try(errors.Wrapf(err, ...))

O simplemente divida la función grande en varias pequeñas.

@faiface en mi opinión en ese momento, uno debería usar if err != nil , pero supongo que es una cuestión de preferencia.

Sin embargo, a veces (como en el caso de inicialización de estructuras) no es una buena idea dividir en varias funciones. Aunque supongo que me estoy poniendo un poco quisquilloso.

En realidad, no estoy súper en contra de try , pero tampoco soy un gran partidario. Creo que existe otra solución mejor.

@deanveloper

Sin embargo, a veces (como en el caso de inicialización de estructuras) no es una buena idea dividir en varias funciones.

Es cierto, pero tampoco es necesario decorarlos de manera diferente, porque toda la decoración específica requerida proviene de parseField .

Creo que existe otra solución mejor.

¡Eso es muy posible! Si veo una mejor solución, soltaré try en un minuto :)

la mayoría de las veces es necesario utilizar la misma decoración para todos los errores dentro de una función porque se debe esperar que las subfunciones proporcionen su propio contexto

@faiface Estoy totalmente en desacuerdo con esta afirmación. Cada función es una subfunción de otra en la pila de llamadas. Significa que tiene las mismas responsabilidades en el flujo de manejo de errores (es decir, proporcionar contexto de error al alcance superior).

Imagine una función que agrega dos fragmentos de datos a un solo archivo. ¿Cómo distinguiría cuál de esos anexos falló si apenas devuelve una declaración "no se pudo escribir en el archivo"?

Todos somos criaturas perezosas. Yo también preferiría hacer algo de una vez por todas si pudiera. Y sí, cuando comencé mi aventura con Go, consideré que el manejo de errores era un poco engorroso. Después de algunos años de práctica, mi vista giró 180 grados. Creo que el manejo de errores actual en Go promueve una programación responsable y un buen diseño. En mi humilde opinión, sería un gran error agregar otro mecanismo que socava este enfoque.

@mklimuk Una parte clave de mi comentario es "la mayor parte del tiempo". El ejemplo que proporcionó probablemente sea mejor manejado por if err != nil . Como se señaló muchas veces, try no está diseñado para manejar todas las situaciones, solo las más comunes.

Y la evidencia muestra que lo hace, ya que el 70% de todo el código de manejo de errores en la biblioteca estándar puede usar try listo para usar y lo mismo ocurre con el 59% de todo el código de manejo de errores en el salvaje.

@faiface bueno, el hecho de que try pueda reemplazar el manejo explícito de errores no significa que deba hacerlo. En mi caso, devolver un error sin agregarle contexto no es 'la situación más común'. Es todo lo contrario :)

A las personas que votan a favor de este hilo les preocupa que esta nueva declaración arruine todo el esfuerzo detrás del diseño original de Go (simplicidad, claridad, etc.) en aras de hacer que el código de Go sea menos detallado.

Seguro, pero espero que comprenda que try no es para devolver un error sin un contexto. De hecho, el caso más común de agregar contexto (un contexto por función) se simplifica enormemente con try :

func doSomething() (err error) {
    defer fmt.HandleErrorf(&err, "doing something")

    x := try(oneThing())
    try(anotherThing(x))
    // ...
}

Lo que hay que tener en cuenta es que la mayoría de las veces, oneThing() y anotherThing() devolverán un contexto suficiente por sí mismos, por lo que envolverlo en un simple "doing something: ..." es completamente suficiente.

Como nota al margen, creo que podríamos usar algunas convenciones sobre _quién_ hace la decoración. En stdlib algunas funciones hacen esto, ex copy: x to y o similar, personalmente he dejado la decoración a la persona que llama, ya que tiene los argumentos.

Por ejemplo, si tuviera un Copy() haría algo como return errors.Wrap(err, "writing") y la persona que llama usando Copy() pondría errors.Wrapf(err, "copying from %v to %v", src, dst) o similar.

Estos dos no se mezclan y combinan demasiado bien, y a veces pueden terminar con cadenas duplicadas, ¿es mejor decir que el estilo stdlib es idiomático? Sin embargo, no recuerdo que todos se comportaran así. Creo que esa es la única forma en que el ejemplo de @faiface sería suficiente. Sin embargo, tal vez he estado invirtiendo el problema, para mí se siente más limpio hacer menos y dejar las decisiones a la persona que llama, especialmente "por si acaso" omiten información contextual.

Personalmente, he dejado la decoración a la persona que llama, ya que tiene los argumentos.

Si. Por ejemplo, considere una función que analiza el cuerpo JSON de una solicitud HTTP, verifica los encabezados, etc. Si se alimenta con JSON sintácticamente inválido, su responsabilidad, todo lo que puede hacer, en realidad, es informar el error. El _caller_ sabe qué parte de la API estaba intentando ser llamado y necesita decorar el error en consecuencia antes de, a su vez, pasarlo por la cadena o emitir un error HTTP.

Si sus funciones están realmente excluidas del código de propósito general que podría usarse en varios lugares, no tendrán la información necesaria para decorar el error. Por el contrario, si tienen todo el contexto, probablemente no sean realmente funciones que tengan sentido como funciones independientes, solo estás creando funciones para dividir el código y hacer que se vea mejor organizado de lo que realmente está.

@lpar ¿Te importa dar un ejemplo específico de esto?

¿Ya di un ejemplo específico? Considere si su función parseJSON realmente conocía el contexto y pudo decorar qué punto final de API y flujo de actividad estaba analizando el cuerpo. Eso sugeriría que era específico para ese punto final o que estaba pasando la información solo para poder usarla para envolver errores.

@lpar Bien, ese es otro ejemplo en el que if err != nil permanecerá en uso. O divide su lógica en múltiples funciones.

Pero entienda que dar un ejemplo donde try no es apropiado no es un argumento en contra de try . try no está destinado a reemplazar todo el manejo de errores, solo los casos más comunes.

Screenshot 2019-07-07 at 6 30 42 PM

@ abejide001 la propuesta try no es la tradicional "prueba / captura" de muchos otros lenguajes, es mucho más similar a la macro try! en Rust. Buen meme aunque lol

Vaya, publicado en el número equivocado. Movido a https://github.com/golang/go/issues/32437#issuecomment -509024693.

Recientemente publiqué una propuesta # 32968 que se basa en mi desacuerdo con la peligrosa capacidad de anidar que posee la macro try . Si bien espero que carezca de defectos graves, como autor no soy la persona adecuada para verlos. Así que me gustaría pedirle a mi campamento _no pruebes_ (tú :) que lo vea, evalúe y comente.


Extracto:

  • _La macro check no es de una sola línea: ayuda más cuando muchos son repetitivos
    las comprobaciones que utilicen la misma expresión deben realizarse muy cerca.
  • _Su versión implícita ya se compila en el patio de recreo._

Restricciones de diseño (cumplidas)

Es una función integrada, no se anida en una sola línea, permite muchos más flujos que try y no tiene expectativas sobre la forma de un código dentro. No fomenta los retornos desnudos.

ejemplo de uso

// 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
  }

Espero que esto ayude, ¡disfrútalo!

Pero entienda que dar un ejemplo donde try no es apropiado no es un argumento en contra de try . try no está destinado a reemplazar todo el manejo de errores, solo los casos más comunes.

Y según las estadísticas que publiqué anteriormente , no son los casos más comunes en mi código. Los casos más comunes en mi código son errores que se envuelven antes de la devolución. Entonces, try solo sería apropiado para un porcentaje de un solo dígito de mis devoluciones de error en el mejor de los casos (*), por lo que creo que necesitamos algo mejor.

(*) Y, de hecho, me inclino a pensar que es probable que las instancias de retorno err desnudas sean errores que deberían corregirse.

Totalmente de acuerdo, deje "if err! = Nil" solo.

@ abejide001 la propuesta try no es la tradicional "prueba / captura" de muchos otros lenguajes, es mucho más similar a la macro try! en Rust. Buen meme aunque lol

Esto solo es una preocupación para mí, Go ya es un lenguaje extraño para los recién llegados, y ahora debemos explicar por qué try tiene una lógica a medida. FWIW, no creo que decir "Rust lo hizo" sea una razón sólida para justificar agregar algo a un idioma, simplemente no es muy conocido.

@como no estaba diciendo eso para justificar la función, solo lo estaba diciendo para aclarar lo que hacía la función. Estoy bastante en el medio con la propuesta try .

No es que esto marque una gran diferencia, pero también es una característica de Swift, aunque con una palabra clave en lugar de una macro.

Parece que existe cierta confusión en cuanto a qué es exactamente intentar lograr. En mi humilde opinión, el problema no es escribir varios bloques if que busquen errores. Los escribe una vez y ya está. El problema es leer código que tiene varios de esos bloques. Leemos mucho más que escribimos. Y estos bloques ofuscan el código real porque se entrelazan con él. Peor aún, muchas veces son casi exactamente iguales, con solo una pequeña diferencia de cadena en algún lugar dentro de ese bloque if.

Personalmente, yo prefería el antiguo cheque - cheque de giro, pero esto al menos hace un buen trabajo separando errores y rutas comerciales. Y finalmente podríamos tener un contexto de alcance de función único en lugar de para cada llamada, que actualmente tiene una buena posibilidad de repetir lo mismo que el error principal.

@icholy escribió:

Hubo una abrumadora retroalimentación de la comunidad que solicitó un manejo de errores más simplificado (de la encuesta anual). El Go Team ahora está abordando ese problema.

Acabo de buscar la encuesta aquí: https://blog.golang.org/survey2018-results

Aparentemente, la pregunta era: "¿Cuál es el mayor desafío al que te enfrentas personalmente al usar Go hoy?" con posible respuesta "Manejo de errores".

Me pregunto seriamente cómo en base a esa pregunta + respuesta se dedujo que se requería una sintaxis más breve. También podría haber respondido 'manejo de errores', pero de ninguna manera hubiera querido ver otra sintaxis. Si hubiera marcado esta opción en la encuesta, habría pensado en permitir mejor ajustar los errores, proporcionarles seguimientos de pila, etc.

Mi sugerencia sería dar un paso atrás de todas las propuestas de manejo de errores (efectivamente, lo que sugería @miekg ). Y primero determine qué es lo que realmente quiere la comunidad, documente eso. Luego averigüe por qué eso es lo que quieren. Y solo después comience a buscar formas de lograrlo.

Acabo de estar revisando la propuesta de prueba, pero a menos que me falte algo, omite decir _por qué_ se está proponiendo, aparte de "eliminar las declaraciones repetitivas if [...}". Pero no se menciona por qué la eliminación de esas declaraciones repetitivas si es necesaria.

Definitivamente estoy de acuerdo con lo anterior. Veamos si los nuevos cambios en los valores de error ayudan a solucionar los errores en el manejo de las quejas que las personas tienen con Go. Entonces podemos ver si se requiere una sintaxis más breve.

La gente aquí está argumentando en contra de try porque sienten que todos los errores devueltos deben anotarse. La realidad es que, en el corpus de código actual (incluida la biblioteca estándar), un alto porcentaje de comprobaciones de errores tienen retornos de error ~ desnudos ~ sin anotaciones y se beneficiarían de try . Su creencia de cómo DEBERÍA ser el código no tiene nada que ver con la forma en que el código ES . Ahórrame tu dogma.

@icholy Ignorar los errores indica que al desarrollador no le importa ese error. El error es insignificante o la persona que llama cree que es imposible. Si ese es el caso, entonces "probar" es igualmente inútil, la persona que llama simplemente no envolvería la función en un "intento".

Mi sugerencia sería dar un paso atrás de todas las propuestas de manejo de errores (efectivamente, lo que sugería @miekg ). Y primero determine qué es lo que realmente quiere la comunidad, documente eso. Luego averigüe por qué eso es lo que quieren. Y solo después comience a buscar formas de lograrlo.

Estoy totalmente de acuerdo con esto. Veo un gran desacuerdo básico sobre qué funcionalidad debería admitir cualquier mejora en el manejo de errores de Go. Cada parte diferente de la funcionalidad que la gente menciona está desencadenando el abandono de bicicletas por su nombre y sintaxis, por lo que la discusión no va a ninguna parte.

Me gustaría saber con más detalle qué es lo que la comunidad de Go en general quiere de cualquier nueva función de manejo de errores propuesta.

He elaborado una encuesta que enumera un montón de características diferentes, piezas de funcionalidad de manejo de errores que he visto proponer a la gente. He omitido cuidadosamente cualquier nomenclatura o sintaxis propuesta y, por supuesto, he intentado que la encuesta sea neutral en lugar de favorecer mis propias opiniones.

Si a la gente le gustaría participar, aquí está el enlace, abreviado para compartir:

https://forms.gle/gaCBgxKRE4RMCz7c7

Todos los que participan deberían poder ver los resultados resumidos. Entonces, tal vez una vez que tengamos una mejor idea de lo que la gente realmente quiere, podremos tener una discusión inteligente sobre si la propuesta de prueba proporciona esas cosas. (Y luego tal vez incluso continúe discutiendo la sintaxis).

@ lane-c-wagner, ¿está tratando de decir que devolver un error no anotado es lo mismo que no devolverlo en absoluto? editar: comentario anterior fijo

@icholy Ah, lo

Esta propuesta sostiene que ninguna acción debe ser una acción válida. Este cambio afecta a todos los usuarios del idioma porque leen el código. Como tal, una encuesta que identifique el obstáculo más grande aún debe preguntar a la comunidad si vale la pena solucionarlo. Esta propuesta es la evaluación más cercana de tal pregunta.

Por favor deje de decir "que todo el mundo es libre de ignorar" try . Leemos código escrito por otros.

@ tv42 No sé si se está dirigiendo a mí aquí , pero también lo he dicho, y tiene razón. Culpable de los cargos. Intentaré ser más cuidadoso con generalizaciones como esa. Gracias.

@griesemer que la encuesta fue muy deficiente. Voté por el manejo de errores, pero el problema al que me refería era seguridad de tipo completo, no verbosidad. Será mejor que hagas otro solo sobre errores.

Y todavía quiero tipos de suma.

Esta es una propuesta sobre la forma en que gofmt formatea actualmente if err! = Nil

(Esta no es una opinión sobre la propuesta try ()).

Cuando una instrucción if devuelve un valor de error no nulo de una línea, como:

err := myFunc()
if err != nil {
    return err
}

gofmt podría relajar su propia regla de declaración if y formatearla en una línea como esta:

err := myFunc()
if err != nil { return err }

Tres líneas de código de manejo de errores se convierten en una sola línea. Menos desorden. Más fácil de seguir el flujo del programa.

Deberá haber algún juicio sobre dónde trazar la línea (juego de palabras reconocido) con este
cambio de regla gofmt. Puede incluir alguna decoración, como:

err := myFunc()
if err != nil { return fmt.Errorf("myFunc() blew up! %v", err }

Pero el manejo elaborado de errores de varias líneas debe permanecer como está: varias líneas, claro y explícito.

La propuesta _try_ ha sido retirada: https://github.com/golang/go/issues/32437#issuecomment -512035919

¿Genéricos alguien?

Esta es una propuesta sobre la forma en que gofmt formatea actualmente if err! = Nil

Lo he intentado, en mi humilde opinión, el código es aún más ilegible de esa manera que con el formato de múltiples líneas. probar es mucho mejor que esa solución.

En mi opinión, el problema aquí no es cómo se realiza el manejo de errores, sino si se ignora . ¿No sería posible dejar la sintaxis if err != nil como está, pero restringir la ignorancia de los retornos Error ? Por ejemplo, conviértalo en una advertencia / error del compilador con la opción de deseveridad para el código heredado.

En mi opinión, el problema aquí no es cómo se realiza el manejo de errores, sino si se ignora . ¿No sería posible dejar la sintaxis if err != nil como está, pero restringir la ignorancia de los retornos Error ? Por ejemplo, conviértalo en una advertencia / error del compilador con la opción de deseveridad para el código heredado.

Mucha gente quiere un linter que muestre los errores ignorados.

Preferiría hacer de esto un error grave, pero mirando las toneladas de legado ya escrito, linter también es justo.

Encuentro https://github.com/kisielk/errcheck valioso por contarme sobre errores no manejados @plyhun @sorenvonsarvort

Como se vio en la discusión sobre # 32437, esta propuesta ha sido efectivamente aceptada por ahora. Clausura. Si el problema vuelve a surgir, se puede abrir una nueva propuesta.

Estoy empezando a pensar que una de las razones por las que muchas de las propuestas parecen no encajar bien es porque en realidad están tratando de abordar dos problemas diferentes al mismo tiempo. Por un lado, es cierto que tener bloques err != nil después de casi cada llamada de función puede romper el flujo del código de una manera extraña, aunque ciertamente tiene sus ventajas, pero creo que eso es solo la mitad de los problema. El otro problema es que manejar múltiples devoluciones, independientemente de si hubo errores involucrados o no, puede ser bastante torpe.

Las funciones de retorno múltiple se sienten muy, muy diferentes de las funciones de retorno único, a pesar de la aparentemente pequeña diferencia entre las dos. Es como si hubiera restricciones adicionales sobre las funciones de llamada que toman más de un argumento. A veces se siente muy extraño lidiar con esto. Cuando llama a una función con múltiples valores de retorno, casi siempre necesita hacerlo en su propia línea, y esto, combinado con := , es a menudo la fuente principal de los diversos problemas de sombreado de variables que se han discutido en otra parte. . No puede encadenar llamadas de método a ellos, no puede asignarlos directamente a un campo de estructura y una nueva variable en la misma línea, y así sucesivamente.

No sé. Quizás sea solo yo. Pero he usado Go durante casi 10 años y las funciones de llamada con múltiples retornos todavía me resultan incómodas a veces.

¡Gracias!

En realidad, hay un problema con if err != nil , el alcance de err puede durar más de lo que debería. Cuando inserta el if , resuelve el problema, pero no todos los casos pueden insertarse.

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 {

La variable err no debería existir en el alcance de la función después de que se complete el bloque if err != nil {} . Aquí está mi propuesta que se basa en la propuesta try() para solucionar el problema https://github.com/golang/go/issues/33161. Me encantaría recibir comentarios constructivos.

La variable err no debería existir en el alcance de la función después de que se complete el bloque if err! = Nil {}.

¿Por qué "debería" no existir después de que se complete el bloque if? El compilador puede optimizarlo (si lo considera necesario), y no hay carga mental cuando el bloque err: = stmt () \ nif err! = Nil {} se completa porque casi siempre van juntos.

Todavía no he analizado tu propuesta en profundidad (¡aunque kudo es por hacer el esfuerzo de escribir una!). Sin embargo, como también describí en mi comentario anterior, creo que se requiere más investigación sobre cualquier problema percibido, antes de profundizar en las propuestas para resolverlo.

Los errores de if err != nil , principalmente porque ya actuamos como si no lo hiciera.

En el ejemplo de CopyFile, hay r, err := os.Open(src) seguido de w, err := os.Create(dst) . El segundo err sigue al primero. El sombreado de variables suele estar mal visto.

También hay otras rarezas. Si tengo err := foo() y luego algo como bar.V, err = baz() , si el código se refactoriza y ya no necesito foo (), necesitaría agregar var err error antes de baz línea. . No creo que la refactorización de una ubicación diferente en una función deba afectar a otros lugares como ese.

Técnicamente en

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

la segunda instancia de err no sombrea la primera instancia. En realidad, son la misma variable. Vea la discusión sobre la redeclaración de variables en https://golang.org/ref/spec#Short_variable_declarations.

func doSomeThing () {
r, err: = os.Open (nombre de archivo)
panic (fmt.Errorf (err, "no se pudo abrir el archivo:% s", nombre del archivo)) //
Es pánico aquí.

}

El jueves, 10 de octubre de 2019 a las 11:24 a.m., clearcode [email protected] escribió:

Creo que podemos agregar una función de construcción:

afirmar()

ejemplo:

func doSomeThing () error {

r, err := os.Open(filename)
assert(err, "failed to open file: %s", filename) // in this step, just return the error

resp, err: = http.Get (someURL)
afirmar (err, "solicitud fallida")

}

y otra función que no devuelve un error:

func doSomeThing () {
r, err: = os.Open (nombre de archivo)
asert (err, "no se pudo abrir el archivo:% s", nombre del archivo) // Es pánico aquí.

}

así que assert (error, args ... interface {}) es mejor que: if err! = nil; {
return err}

-
Estás recibiendo esto porque hiciste un comentario.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/golang/go/issues/32825?email_source=notifications&email_token=AGUV7XQ5HO7GL3YP72R7BV3QN2N55A5CNFSM4H4DL33KYY3PNVWWK3TUL52HS4DFVREXG43KUMVBW2
o darse de baja
https://github.com/notifications/unsubscribe-auth/AGUV7XS4JMK44QHIIR3RSGTQN2N55ANCNFSM4H4DL33A
.

La conclusión es que quiero ver el error real devuelto en el actual
función en la línea actual.

El viernes, 11 de octubre de 2019 a las 9:55 a.m., Aaaa Einai [email protected] escribió:

func doSomeThing () {
r, err: = os.Open (nombre de archivo)
panic (fmt.Errorf (err, "no se pudo abrir el archivo:% s", nombre del archivo)) // Es pánico aquí.

}

El jueves, 10 de octubre de 2019 a las 11:24 a. M. Clearcode [email protected]
escribió:

Creo que podemos agregar una función de construcción:

afirmar()

ejemplo:

func doSomeThing () error {

r, err := os.Open(filename)
assert(err, "failed to open file: %s", filename) // in this step, just return the error

resp, err: = http.Get (someURL)
afirmar (err, "solicitud fallida")

}

y otra función que no devuelve un error:

func doSomeThing () {
r, err: = os.Open (nombre de archivo)
asert (err, "no se pudo abrir el archivo:% s", nombre del archivo) // Es pánico aquí.

}

así que assert (error, args ... interface {}) es mejor que: if err! = nil; {
return err}

-
Estás recibiendo esto porque hiciste un comentario.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/golang/go/issues/32825?email_source=notifications&email_token=AGUV7XQ5HO7GL3YP72R7BV3QN2N55A5CNFSM4H4DL33KYY3PNVWWK3TUL52HS4DFVREXG43KUMVBW2
o darse de baja
https://github.com/notifications/unsubscribe-auth/AGUV7XS4JMK44QHIIR3RSGTQN2N55ANCNFSM4H4DL33A
.

Francamente, no quiero un retorno implícito que proporciona try . Si tuviéramos genéricos, preferiría una solución que usara el comportamiento monad-ish en su lugar.

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

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

Para mí, esto es mucho más limpio que el incorporado que se propone actualmente, aunque se requerirían más cambios en lo anterior, ya que tendría una proliferación de métodos que devolvieron (valor, error) y Resultado

Es muy similar a Ok y Err de Rust.
Creo que if err != nil {} tal vez sea mejor un poco.

@Yanwenjiepy eso es intencional, soy un gran admirador del tipo Result de Rust.

Llevo menos de 10 minutos aprendiendo Go. Lo primero que noté en el código que estaba viendo fue que esta copia se pegó una y otra vez:

someValue, err := someFunction();
if err != nil {
  panic(err)
}

Obviamente no soy un experto, pero podría ser valioso que solo me haya tomado mi primera mirada terminar en este hilo.

Eso es porque está buscando fragmentos de código para aprender. El código real tiene que manejar errores, no solo entrar en pánico y fallar.

Es cierto, pero los errores pueden (y a menudo deben) agruparse. Es por eso que los bloques try / catch existen en otros idiomas. Por ejemplo, lo siguiente para mí olería mucho menos a dinosaurio:

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();
}

Una vez más, total laico. Pero el código son solo símbolos, y si se repiten lo suficiente, también puede hacer un símbolo para eso. Este parece ser un símbolo que se repite con mucha frecuencia.

Es posible que desee echar un vistazo a la publicación Los errores son valores de Rob Pike para ver cómo puede usar un ayudante para fusionar errores y tratarlos todos a la vez. En la práctica, detectar todas las excepciones con una sola cláusula se considera de mal estilo en la mayoría de los lenguajes que las tienen, porque terminas ocultando información sobre lo que realmente sucedió. (Y si extiende el ejemplo para desglosar las excepciones detectadas individuales y no desecha esa información, el código termina siendo tan largo como el equivalente de Go).

Gracias por el enlace. El errWriter es una solución totalmente aceptable.

Es cierto, pero los errores pueden (y a menudo deben) agruparse. Es por eso que los bloques try / catch existen en otros idiomas. Por ejemplo, lo siguiente para mí olería mucho menos a dinosaurio:

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();
}

Una vez más, total laico. Pero el código son solo símbolos, y si se repiten lo suficiente, también puede hacer un símbolo para eso. Este parece ser un símbolo que se repite con mucha frecuencia.

Digamos que cada función devuelve un tipo de error superpuesto y debe manejar todos los resultados de la función con elegancia, ¿cómo se escribe 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?
}

A otra persona solo le tomará 1 minuto entender el siguiente código:

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
}

Digamos que cada función devuelve un tipo de error superpuesto y debe manejar todos los resultados de la función con elegancia

En ese ejemplo, tienes toda la razón.

¿Fue útil esta página
0 / 5 - 0 calificaciones