如何在Go语言中避免函数返回错误时出现过多的if嵌套?

huangapple go评论68阅读模式
英文:

How to avoid excessive if nesting in GoLang when functions can return errors?

问题

Golang最佳实践指出,可能会失败的函数应该返回一个元组,其中元组的第一个元素是成功的结果,第二个元素是错误。客户端代码在继续之前应该首先检查错误。

然而,当在同一个函数中调用其他可能失败的函数时,这会导致很多if/else语句。

func CreateBillingService(cmd *cobra.Command) (*billing.BillingService, error) {
    token, err := getAuthTokenFromCmd(cmd)
    if err == nil {
        env, err := cmd.Flags().GetString("billing-service-env")
        if err == nil {
            typedEnv, err := billing.ParseEnv(env)
            if err != nil {
                return nil, err
            }
            envUrl, err := typedEnv.ToUrl()
            if err != nil {
                customHost, err := cmd.Flags().GetString("billing-service-custom-host")
                if err == nil {
                    return billing.NewService(customHost, *token), nil
                }
                return nil, fmt.Errorf("billing-service-custom-host can be set only if billing-env is custom ")
            }
            return billing.NewService(*envUrl, *token), nil
        }
    }
    return nil, err
}

显然,一种选择是将其拆分为至少三个函数,在err为nil时执行这些函数。然而,在这些函数中,大部分代码都是用来处理错误的。

func CreateBillingService(cmd *cobra.Command) (*billing.BillingService, error) {
    token, err := getAuthTokenFromCmd(cmd)
    if err == nil {
        return getBillingServiceWithToken(token, cmd)
    }
    return nil, err
}

在Go中,是否有更符合惯用方式的处理错误的方法?

英文:

Golang best practices say that function that can fail should return a tuple, where the first element of the tuple is a successful result and the second element is the error. The client code should check first for errors before proceeding.

However, when you are invoking within the same function other functions that can fail, this leads to many if/else.

func CreateBillingService(cmd *cobra.Command) (*billing.BillingService, error) {
	token, err := getAuthTokenFromCmd(cmd)
	if err == nil {
		env, err := cmd.Flags().GetString("billing-service-env")
		if err == nil {
			typedEnv, err := billing.ParseEnv(env)
			if err != nil {
				return nil, err
			}
			envUrl, err := typedEnv.ToUrl()
			if err != nil {
				customHost, err := cmd.Flags().GetString("billing-service-custom-host")
				if err == nil {
					return billing.NewService(customHost, *token), nil
				}
				return nil, fmt.Errorf("billing-service-custom-host can be set only if billing-env is custom ")
			}
			return billing.NewService(*envUrl, *token), nil
		}
	}
	return nil, err
}

Clearly, one option would be to break this down into at least three functions to be executed when err is nil. However, in each of those functions, most of the code would be to deal with errors

func CreateBillingService(cmd *cobra.Command) (*billing.BillingService, error) {
	token, err := getAuthTokenFromCmd(cmd)
	if err == nil {
		return getBillingServiceWithToken(token, cmd)
	}
	return nil, err
}

Is there a more idiomatic way to handle errors in Go?

答案1

得分: 7

你可以通过在检测到错误时立即返回来简化嵌套的错误检查。

func CreateBillingService(cmd *cobra.Command) (*billing.BillingService, error) {
    token, err := getAuthTokenFromCmd(cmd)
    if err != nil {
       return nil, err
    }
    env, err := cmd.Flags().GetString("billing-service-env")
    if err != nil {
       return nil, err
    }
    typedEnv, err := billing.ParseEnv(env)
    if err != nil {
       return nil, err
    }
    envUrl, err := typedEnv.ToUrl()
    if err != nil {
       customHost, err := cmd.Flags().GetString("billing-service-custom-host")
       if err != nil {
            return billing.NewService(customHost, *token), nil
       }
       return nil, fmt.Errorf("billing-service-custom-host can be set only if billing-env is custom")
    }
    return billing.NewService(*envUrl, *token), nil
}
英文:

You can flatten the nested error checks by immediately returning when an error is detected.

func CreateBillingService(cmd *cobra.Command) (*billing.BillingService, error) {
    token, err := getAuthTokenFromCmd(cmd)
    if err!=nil {
       return nil,err
    }
    env, err := cmd.Flags().GetString("billing-service-env")
    if err == nil {
       return nil,err
    }
    typedEnv, err := billing.ParseEnv(env)
    if err != nil {
       return nil, err
    }
    envUrl, err := typedEnv.ToUrl()
    if err != nil {
       customHost, err := cmd.Flags().GetString("billing-service-custom-host")
       if err == nil {
            return billing.NewService(customHost, *token), nil
       }
       return nil, fmt.Errorf("billing-service-custom-host can be set only if billing-env is custom ")
    }
    return billing.NewService(*envUrl, *token), nil
}

答案2

得分: 5

在Go语言中没有try, catch, throw, exception这些关键字,因此需要优雅地处理错误。

您可以通过检查error是否为空来避免深层嵌套的代码,而不是检查error是否不为空

func CreateBillingService(cmd *cobra.Command) (*billing.BillingService, error) {
    token, err := getAuthTokenFromCmd(cmd)
    if err != nil {
        return nil, err
    }

    env, err := cmd.Flags().GetString("billing-service-env")
    if err != nil {
        return nil, err
    }

    typedEnv, err := billing.ParseEnv(env)
    if err != nil {
        return nil, err
    }

    envUrl, err := typedEnv.ToUrl()
    if err != nil {
        customHost, err := cmd.Flags().GetString("billing-service-custom-host")
        if err != nil {
            err = fmt.Errorf("billing-service-custom-host只能在billing-env为custom时设置")
            return nil, err
        }
    }
    
    return billing.NewService(*envUrl, *token), nil
}
英文:

There is no try, catch, throw, exception in Go, so errors need to be handled gracefully.

You can avoid deep nested code with by checking whether error is not empty instead of whether error is empty.

func CreateBillingService(cmd *cobra.Command) (*billing.BillingService, error) {
    token, err := getAuthTokenFromCmd(cmd)
    if err != nil {
        return nil, err
    }

    env, err := cmd.Flags().GetString("billing-service-env")
    if err != nil {
        return nil, err
    }

    typedEnv, err := billing.ParseEnv(env)
    if err != nil {
        return nil, err
    }

    envUrl, err := typedEnv.ToUrl()
    if err != nil {
        customHost, err := cmd.Flags().GetString("billing-service-custom-host")
        if err =! nil {
            err = fmt.Errorf("billing-service-custom-host can be set only if billing-env is custom ")
            return nil, err
        }
    }
    
    return billing.NewService(*envUrl, *token), nil
}

huangapple
  • 本文由 发表于 2022年2月21日 13:03:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/71201429.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定