为了账号安全,请及时绑定邮箱和手机立即绑定

Golang 混合赋值和声明

Golang 混合赋值和声明

Go
函数式编程 2022-01-10 17:38:04
我开始使用 go 工作了几个星期,并且(再一次)我偶然发现了一些对我来说似乎很奇怪的东西:// Not workinga := 1{    a, b := 2, 3}// Worksa := 1a, b := 2, 3我想同时分配两个变量。一个已经在上级范围内声明,另一个则没有。它不起作用:编译器尝试重新声明前一个变量。但是,如果在同一范围内声明此变量,则它可以正常工作。这是为什么?
查看完整描述

3 回答

?
梵蒂冈之花

TA贡献1900条经验 获得超5个赞

您所遇到的通常称为“可变阴影”。当您:=在内部范围内使用任何变量时,包括在语句中if,for尽管没有大括号,但新值和类型与该变量相关联:


n := "Example"

//Prints the string variable `n` to standard output and

// returns the number of bytes written in int variable `n` and

// an error indicator in error variable `err`.

if n, err := fmt.Println(n); err != nil {

    panic(err)

} else {

    fmt.Println(n, "bytes written")

}


//Prints the string variable `n` to standard output.

fmt.Printf("n = %q\n", n)

输出:


Example

8 bytes written

n = "Example"

有几种不同的方法可以解决此问题:

  • 在使用它们之前声明您需要的变量并使用普通赋值 =

  • 使用不同的变量名

  • 创建一个新范围并保存变量的值以供以后访问,根据需要使用变量名称:=,并在范围结束之前恢复该值;使用不同的变量名通常更容易,因为无论如何您都在创建另一个变量

也可能发生相反的效果,您在内部范围内声明某些内容但没有意识到:

if _, err := fmt.Println(n); err != nil {

    panic(err)

} else {

    fmt.Println(n, "bytes written")

}


//undefined: err

if _, err = fmt.Println(n); err != nil {

    //undefined: err

    panic(err)

}

  • 在使用它们之前声明您需要的变量并使用普通赋值 =

  • 将第一个:=if语句分开,因此变量按预期声明;这允许您=在该范围的上下文以及包含它的任何范围内使用该变量的所有其他实例

  • 更改=to 的所有实例:=以修复错误

请注意,当函数返回多个值时,您可能会在最后两种情况下遇到变量阴影问题,但这可以按照上面的说明解决。


在 Go Playground 上尝试这两个示例。


您的最后一个示例说明了声明和初始化新变量b同时还为现有变量赋值的组合a。没有创建新范围,因此您不会隐藏原始变量a,您可以通过a在每次分配之后(但在下一个声明/分配之前)打印地址来验证:


a := 1

fmt.Println(&a)

a, b := 2, 3

fmt.Println(&a)

a = b          // avoids a "declared but not used" error for `b`

当然,如果你没有声明b,那么你会从编译器收到一个错误,即:=第二个声明的左侧没有新变量,这是一种迂回的说法,你正在尝试声明a在同一范围内两次。


请注意,如果仔细应用此想法,也可用于查找被遮蔽的变量。例如,您的示例中的“不工作”代码将打印不同的地址a,具体取决于a内部范围内部是否已声明:


a := 1

{

    fmt.Println(&a)    // original `a`

    a, b := 2, 3

    fmt.Println(&a)    // new `a`

    a = b              // avoids a "declared but not used" error for `b`

}

fmt.Println(&a)        // original `a`


查看完整回答
反对 回复 2022-01-10
?
回首忆惘然

TA贡献1847条经验 获得超11个赞

根据 golang 的文档:


在块中声明的标识符可以在内部块中重新声明。


这正是您的示例所显示的内容,由于“:=”,因此在括号内重新声明了 a,并且从未使用过。


一个解决方案是声明两个变量然后使用它:


var a, b int

{

    b, a = 2, 3

    fmt.Println(b)

}

fmt.Println(a)


查看完整回答
反对 回复 2022-01-10
?
慕沐林林

TA贡献2016条经验 获得超9个赞

您的问题有两部分:

第一部分:

= 只是分配

:= 是为功能块(非全局)内的新变量(至少一个新变量)定义和分配,工作示例:


package main


import (

    "fmt"

)


func main() {

    var u1 uint32      //declare a variable and init with 0

    u1 = 32            //assign its value

    var u2 uint32 = 32 //declare a variable and assign its value at once

    //declare a new variable with defining data type:

    u3 := uint32(32)        //inside the function block this is equal to: var u3 uint32 = 32

    fmt.Println(u1, u2, u3) //32 32 32

    //u3 := 20//err: no new variables on left side of :=

    u3 = 20

    fmt.Println(u1, u2, u3)       //32 32 20

    u3, str4 := 100, "str"        // at least one new var

    fmt.Println(u1, u2, u3, str4) //32 32 100 str

}

第二部分:

在块中声明的标识符可以在内部块中重新声明。

这里有 4 个不同的可变范围和阴影工作示例:


限制变量范围的简单方法:


package main

import "fmt"

func main() {

    i := 1

    j := 2

    //new scope :

    {

        i := "hi" //new local var

        j++

        fmt.Println(i, j) //hi 3

    }

    fmt.Println(i, j) //1 3

}

使用函数调用限制变量范围:


package main

import "fmt"

func fun(i int, j *int) {

    i++                //+nice: use as local var without side effect

    *j++               //+nice: intentionally use as global var

    fmt.Println(i, *j) //11 21

}

func main() {

    i := 10 //scope: main

    j := 20

    fun(i, &j)

    fmt.Println(i, j) //10 21

}

在语句中使用简写赋值:


package main

import "fmt"

func main() {

    i := 10 //scope: main

    j := 4

    for i := 'a'; i < 'b'; i++ {

        fmt.Println(i, j) //97 4

    }

    fmt.Println(i, j) //10 4


    if i := "test"; len(i) == j {

        fmt.Println(i, j) // i= test , j= 4

    } else {

        fmt.Println(i, j) //test 40

    }

    fmt.Println(i, j) //10 4

}

阴影全局变量:


package main

import "fmt"

var i int = 1 //global

func main() {

    j := 2

    fmt.Println(i, j) //1 2

    i := 10           //Shadowing global var

    fmt.Println(i, j) //10 2

    fun(i, j)         //10 2

}

func fun(i, j int) {

    //i := 100   //no new variables on left side of :=

    fmt.Println(i, j) //10 2

}


查看完整回答
反对 回复 2022-01-10
  • 3 回答
  • 0 关注
  • 163 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信