Maison  >  Article  >  développement back-end  >  Allez essayer La nouvelle proposition est-elle fiable ? Vous souhaitez simplifier la gestion des erreurs

Allez essayer La nouvelle proposition est-elle fiable ? Vous souhaitez simplifier la gestion des erreurs

Golang菜鸟
Golang菜鸟avant
2023-08-04 17:05:371254parcourir


Récemment, la nouvelle proposition d'essai "proposition : Go 2 : gestion des erreurs : instruction try avec gestionnaire[1]" a déclenché des discussions animées dans la communauté, combattons à nouveau la communauté ! ! !

Aujourd'hui, Jianyu va l'ouvrir avec tout le monde et voir si cela peut ouvrir et réorganiser le mécanisme de gestion des erreurs Go.

Contexte

L'auteur de la proposition @Greg Weber de PingCAP le fera sur la base de deux facteurs. L'un est clairement mentionné dans "Go Developer Survey 2022 Q2 Results[2]".

Allez essayer La nouvelle proposition est-elle fiable ? Vous souhaitez simplifier la gestion des erreurs

Avec la sortie des génériques Go1.18, les génériques originaux les plus contradictoires ont reçu une solution préliminaire. Selon des enquêtes communautaires, le plus grand défi auquel sont confrontés les développeurs lors de l'utilisation de Go est désormais la gestion des erreurs, et de l'énergie doit être investie pour le « résoudre ».

Un autre facteur est qu'il est bien connu que le code de gestion des erreurs Go est relativement lourd. Les ingénieurs plaisantent souvent en disant que 30 % d'un projet Go a if err = nil.

Le code suivant :

_, err := f()
if err != nil {
    ...
}
_, err = r()
if err != nil {
    ...
}
_, err = w()
if err != nil {
    ...
}

J'espère le rendre plus élégant. De nombreux amis sont également d'accord avec cette conception. Il s'agit en effet d'un traitement simple et intuitif, qui a suscité un débat dans la communauté.

proposition de gestionnaire d'essai

La solution mentionnée dans cette proposition est d'ajouter une nouvelle instruction try pour obtenir une gestion concise des erreurs et rendre le traitement de if err != nil fluide.

Le code suivant :

try err, handler

Le code généré par le compilateur :

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

Dans la fonction, il peut être comme suit :

func(args...) (rtype1, rtypes..., rtypeN, error) {
    try err, handler
    ...
}

Le code généré après traduction :

func(args...) (rtype1, rtypes..., rtypeN, error) {
    if err != nil {
            return Zero(rtype1), Zeros(rtypes...)..., Zero(rtypeN), handler(err)
    }
    ...
}

Il ne peut également être traité que si erreur != nul. Le code suivant :

try err

Le code traduit :

if err != nil {
    return err
}

n'appellera pas le gestionnaire inexistant pour le traitement et reviendra directement. Trois lignes (si err != logique nulle) changent directement 3 mots (essayez).

Si vous ne souhaitez pas écrire de fonction, vous pouvez aussi directement :

x, err := f()
try err, fmt.Errorf("f fail: %w", err)

Le scénario defer+try peut être le suivant :

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

Les paramètres d'entrée sont plus flexibles. L'auteur espère que c'est générique, donc. qu'il peut s'adapter aux exigences de divers scénarios.

示例和实践

针对本提案,原作者给出了各类使用场景的示例。如下代码:

import (
    "fmt"
)

// This helper should be defined in the fmt package
func Handlew(format string, args ...any) func(error) error {
 return func(err error) error {
  args = append(args, err)
  return fmt.Errorf(format+": %w", args...)
 }
}

// This helper should be defined in the fmt package
func Handlef(format string, args ...any) func(error) error {
 return func(err error) error {
  args = append(args, err)
  return fmt.Errorf(format+": %v", args...)
 }
}

func valAndError() (int, error) {
    return 1, fmt.Errorf("make error")
}

func newGo() (int, error) {
    x, err := valAndError()
    try err

    // Common formatting functions will already be provided
    i := 2
    x, err = valAndError()
    try err, Handlew("custom Error %d", i)

    // Using a custom error type
    // For convenience the error type can expose a method to set the error
    x, err = valAndError()
    try err, TheErrorAsHandler(i)
}

type TheError struct{
    num int
    err error
}

func (t TheError) Error() String {
    return fmt.Sprintf("theError %d %v", t.num, t.err)
}

func TheErrorAsHandler(num int) func(err) TheError {
    return func(err error) TheError {
        return theError{ num: i, err: err }
    }
}

另外在日常的 Go 工程中,提案作者认为 CopyFile 函数是新提案语句的一种很好的实践。为此基于 try-handler 进行了一版改造和说明。

如下代码:

// This helper can be used with defer
func handle(err *error, handler func(err error) error) {
    if err == nil {
        return nil
    }
    *err = handler(err)
}

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

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

    w, err := os.Create(dst)
    try err, func(err error) error {
            os.Remove(dst) // only if Create fails
            return fmt.Errorf("dir %s: %w", dst, err)
    }
    defer w.Close()

    err = io.Copy(w, r)
    try err
    err = w.Close()
    try err
    return nil
}

引入 try-hanlder 后,能够做到:

  • 插入错误的返回语句,进行机制预设。
  • 在返回错误之前将错误处理函数组合在一起,便于后续的处理。

总结

在这个新提案中,一旦实施,就可以减少如下代码的编写:

if err != nil {
  return ...
}

在代码编写上会节省一些行数,且可以为错误处理机制引入一些新的 ”操作“,这是该提案的优势。

但是从 Go 开发者的角度而言,会引入一些新的副作用,例如:初学者的学习成本、Go 工具链的改造、程序理解的复杂度增加。

另外新的语句,似乎比较难与 Go1.13 引入的 error.Is 和 As 有较好的相关联性。如果是做一个第三方用户库引入倒可以,但若是作为标准进入 Go 源代码中,似乎又有些格格不入(提案作者希望进入)。

看了那么多提案,Go 错误处理机制的 ”升级“,似乎陷入了手心手背都是肉的阶段...

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer