错误的值在if语句之外消失

时间:2018-04-09 10:42:47

标签: variables go scope

我有以下代码:

if err == nil {
    body, err := ioutil.ReadAll(response.Body)
    if err == nil {
        dataMap := &models.UserResponse{}
        json.Unmarshal(body, &dataMap)
        if dataMap.User == (models.UserId{}) {
            err = fmt.Errorf("unauthorized")
            fmt.Println(err) // when unathorized, prints unauthorized
        }
    }
}

fmt.Println(err) // always prints nil

Printlnif dataMap.User ...打印"unauthorized",而最后Println始终打印nil

我不知道为什么会这样,err在此函数的开头通过var err error声明。

2 个答案:

答案 0 :(得分:7)

原因详见Spec: Short variable declaration:

  

与常规变量声明不同,短变量声明可以重新声明变量,前提是它们最初在同一块中声明(或者如果块是函数体的参数列表)具有相同的类型,并且至少有一个非空白变量是新的。因此,重新声明只能出现在多变量简短声明中。重新声明不会引入新变量;它只是为原始版本赋予了一个新值。

当使用包含多个变量的短变量声明时,如果在同一个块中声明了,则只会对现有变量进行赋值。由于在err块之前存在if变量,将在err块内创建一个新的if变量,其中没有任何内容处理“局外人”err变量(除了共享其名称)。在短变量声明之后,err块中的外部if将被阴影

那么,在if内部,您创建一个新的err变量,并为其指定一个值,然后打印出来。

if语句后,您将在err块内打印其值未更改的外if变量,因此它仍为nil

见这个例子:

var err error
fmt.Println("outside:", err) // nil

{
    // A new err variable, shadows the outer:
    i, err := strconv.Atoi("a")
    fmt.Println("inside:", i, err) // 0 strconv.Aoti: parsing "a": invalid syntax
}

fmt.Println("outside:", err) // nil, because this was never changed

// Now this will change the "outer" err:
j, err := strconv.Atoi("a")
fmt.Println("outside:", j, err) // 0 strconv.Aoti: parsing "a": invalid syntax

输出(在Go Playground上尝试):

outside: <nil>
inside: 0 strconv.Atoi: parsing "a": invalid syntax
outside: <nil>
outside: 0 strconv.Atoi: parsing "a": invalid syntax

如果要在创建新变量时使用(赋值)“外部”变量,则不能在“嵌套”块中使用短变量声明,而只能使用简单赋值,在这种情况下,您也可以必须先验地声明其他变量,如下例所示:

if err == nil {
    var body []byte
    body, err = ioutil.ReadAll(response.Body)
    // ... rest...
}

参见相关问题:

Why it is possible to redefine err in multiple return statement in Go

Why there are two ways of declaring variables in Go, what's the difference and which to use?

答案 1 :(得分:-1)

您已在块内创建err,但您在块外打印相同内容。在块之外声明错误并在其中的任何位置初始化它将在打印时获取值。

var err error
fmt.Println(err) // always prints nil