Go习惯用法(多值赋值短变量声明赋值简写模式)基础实例

 更新时间:2024年01月17日 11:23:51   作者:程序员的自我进化  
本文为大家介绍了Go习惯用法(多值赋值,短变量声明和赋值,简写模式、多值返回函数、comma,ok 表达式、传值规则)的基础实例,帮大家巩固扎实Go语言基础

1. 多值赋值

可以一次性声明多个变量,并可以在声明时赋值,而且可以省略类型,但必须遵守一定的规则要求。

package main 
import "fmt"
func main() {
    var x, y int  // 相同类型变量可以在末尾带上类型
    var a, b int = 1, 2
    var c, d = 3, 4 // 不带类型时,编译器自动推断
    var (  // 不同类型变量声明和隐式初始化
        e int
        f string
        )
    var g int, e int = 6, 7  // 多赋值语句中每个变量后面不能都带上类型
    fmt.Println("x ", x)
    fmt.Println("y ", y)
    fmt.Println("a ", a)
    fmt.Println("b ", b)
    fmt.Println("c ", c)
    fmt.Println("d ", d)
    fmt.Println("e ", e)
    fmt.Println("f ", f)
}

有如下错误:

.\main.go:14:14: syntax error: unexpected comma at end of statement

2. 短变量的声明和赋值

Go  语言的语法允许多值短变量声明和赋值的多个变量中,只要有一个是新变量就可以使用 := 进行赋值。

也就是说,在多值短变量的声明和赋值时, 至少有一个变量是新创建的局部变量,其他的变量可以复用以前的变量,不是新创建的变量执行的仅仅是赋值。

package main 

import "fmt"

func main() {
    var a int = 0
    var b int = 0
    a, b := 1, 2
    fmt.Println("a ", a)
    fmt.Println("b ", b)
}

会有以下错误:

 .\main.go:8:10: no new variables on left side of :=

要想通过编译, a, b := 1, 2 至少一个变量是新定义的局部变量,如果在赋值语句 a, b := 1, 2  中已经存在一个局部变量 a ,则赋值语句不会创建新的变量 a , 而是使用 1 赋值给已经声明的局部变量。但是会创建新的变量 b 并给其赋值。

赋值操作符  和 :=  的区别:

  •  不会声明并创建新变量,而是在当前赋值语句所在的作用域由内向外逐层去搜寻变量,如果没有搜索到相同的变量名,则报编译错误。

  • :=  必须出现在函数或者类型方法内部。

  • :=  至少要创建一个局部变量并初始化。

多值短变量声明赋值 := 的最佳使用场景是在错误处理上。例如:

a, err := f()
if err ! = nil {
    // do something
}
// 此时 err 可以是已存在的 err 变量,只是重新赋值了
b, err := g()

3. 简写模式

Go  语言很多重复的引用或声明可以用 ()  进行简写。

import 多个包;

// 推荐写法
import (
    "os"
    "fmt"
)
// 不推荐写法
import "os"
import "fmt"

多个变量声明;

包中多个相关全局变量声明时,建议使用 () 进行合并声明

// 推荐写法
var (
    uploadStatus bool
    downStatus bool
)
// 不推荐写法
var uploadStatus bool
var uploadStatus bool

4. 多值返回函数

多值返回函数里如果有 error  或 bool  类型的返回值,则应该将 error 和 bool 作为最后一个返回值。这是一种编程风格,没有对错。

buffer.go:107: func (b *Buffer) tryGrowByReslice(n int) (int, bool) {
buffer.go:335: func (b *Buffer) ReadByte() (byte , error) {

5. comma,ok 表达式

常见的几个 comma, ok 表达式用法有以下几种情况:

获取 map 值

获取 map 中不存在键的值不会发生异常,而是会返回值类型的零值,如果想确定 map 中是否存在某个 key,则可以使用获取 map 值的 comma, ok 语法。示例如下:

m := make(map[string] string)
v, ok := m["camera"]
if ok {
    Println("camera exist")
} else {
    Println("camera not exist")
}

读取 chan 值

读取已经关闭的通道不会阻塞,也不会引起 panic, 而是一直返回该通道的零值,判断通道关闭有两种方法,一种是 comma, ok 表达式;另一种是通过 range 循环迭代。

c := make(chan int)
go func() {
    c <- 1
    c <- 2
    close(c)
}()
for {
    v, ok := <- c
    if ok {
        Println("v exist")
    } else {
        Println("v not exist")
    }
}
for v := range c {
    Println(v)
}
  • 类型断言

如果 map 查找、类型断言或通道接收出现在赋值语句的右边,它们都可能会产生两个结果,有一个额外的布尔结果表示操作是否成功:

v, ok = m[key]             // map lookup
v, ok = x.(T)              // type assertion
v, ok = &lt;-ch               // channel receive

注意:map 查找、类型断言或通道接收出现在赋值语句的右边时,并不一定是产生两个结果,也可能只产生一个结果。对于只产生一个结果的情形, map 查找失败时会返回零值,类型断言失败时会发生运行时 panic 异常,通道接收失败时会返回零值(阻塞不算是失败)。例如下面的例子:

v = m[key]                // map查找,失败时返回零值
v = x.(T)                 // type断言,失败时panic异常
v = &lt;-ch                  // 管道接收,失败时返回零值(阻塞不算是失败)
_, ok = m[key]            // map返回2个值
_, ok = mm[""], false     // map返回1个值
_ = mm[""]                // map返回1个值

和变量声明一样,我们可以用下划线空白标识符_来丢弃不需要的值。

_, err = io.Copy(dst, src) // 丢弃字节数
_, ok = x.(T)              // 只检测类型,忽略具体值

6. 传值规则

Go  只有一种参数传递规则,那就是值拷贝,这种规则包括两种含义:

  • 函数参数传递时使用的是值拷贝。

  • 实例赋值给接口变量,接口对实例的引用是值拷贝。

有时在明明是值拷贝的地方,结果却修改了变量的内容,有以下两种情况:

  • 直接传递的是指针。指针传递同样是值拷贝,但指针和指针副本的值指向的地址是同一个地方,所以能修改实参值。

  • 参数是复合数据类型,这些复合数据类型内部有指针类型的元素,此时参数的值拷贝并不影响指针的指向。

Go  复合类型中 chan  、 map  、 slice  、 interface  内部都是通过指针指向具体的数据,这些类型的变量在作为函数参数传递时,实际上相当于指针的副本。

package main
import "fmt"
func main() {
    var x, y int = 3, 5
    fmt.Printf("befor swap x is %d\n", x)
    fmt.Printf("befor swapy is %d\n", y)
    x, y = swap_value(x, y)
    fmt.Printf("after swap x is %d\n", x)
    fmt.Printf("after swap y is %d\n", y)
    x, y = swap_reference(&x, &y)
    fmt.Printf("after swap_reference x is %d\n", x)
    fmt.Printf("after swap_reference y is %d\n", y)
}
func swap_value(x int, y int) (int, int) {
    var tmp int
    tmp = x
    x = y
    y = tmp
    return x, y
}
func swap_reference(x *int, y *int) (int, int) {
    var tmp int
    tmp = *x
    *x = *y
    *y = tmp
    return *x, *y
}

输出:

befor swap x is 3
befor swapy is 5
after swap x is 5
after swap y is 3
after swap_reference x is 3
after swap_reference y is 5

以上就是Go习惯用法(多值赋值短变量声明赋值简写模式)基础实例的详细内容,更多关于Go多值赋值短变量声明的资料请关注脚本之家其它相关文章!

相关文章

  • golang map的基本操作及定义方式

    golang map的基本操作及定义方式

    这篇文章主要介绍了golang-map的基本操作,由于map是引用类型,所以在操作的时候,必须先初始化,本文通过多种方式给大家讲解map的定义方式,需要的朋友可以参考下
    2022-08-08
  • 在Go语言开发中实现高性能的分布式日志收集的方法

    在Go语言开发中实现高性能的分布式日志收集的方法

    本文介绍了在Go语言开发中实现高性能分布式日志收集的关键步骤和考虑因素,包括日志生成与采集、日志传输、日志收集器的高性能网络I/O、日志存储与分析、监控与告警系统、扩展性与可维护性等方面,本文给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2025-01-01
  • gtoken替换jwt实现sso登录的排雷避坑

    gtoken替换jwt实现sso登录的排雷避坑

    这篇文章主要为大家介绍了gtoken替换jwt实现sso登录的排雷避坑,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • golang使用正则表达式解析网页

    golang使用正则表达式解析网页

    这篇文章主要介绍了golang使用正则表达式解析网页,需要的朋友可以参考下
    2015-03-03
  • Golang实现四种负载均衡的算法(随机,轮询等)

    Golang实现四种负载均衡的算法(随机,轮询等)

    本文介绍了示例介绍了Golang 负载均衡的四种实现,主要包括了随机,轮询,加权轮询负载,一致性hash,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • go语言区块链学习调用以太坊

    go语言区块链学习调用以太坊

    这篇文章主要为大家介绍了go语言区块链学习如何调用以太坊的示例实现过程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2021-10-10
  • Go语言中println和fmt.Println区别

    Go语言中println和fmt.Println区别

    本文主要介绍了Go语言中println和fmt.Println区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • 文字解说Golang Goroutine和线程的区别

    文字解说Golang Goroutine和线程的区别

    goroutine 是 Go语言中的轻量级线程实现,由 Go 运行时(runtime)管理,使用每一个 go 关键字将会额外开启一个新的协程 goroutine,今天通过本文给大家介绍下Golang Goroutine和线程的区别,感兴趣的朋友一起看看吧
    2022-03-03
  • 代码之美:探索Go语言断行规则的奥秘

    代码之美:探索Go语言断行规则的奥秘

    Go语言是一门以简洁、清晰和高效著称的编程语言,而断行规则是其代码风格的重要组成部分,通过深入研究Go语言的断行规则,我们可以更好地理解和编写优雅的代码,本文将从语法规范、代码风格和最佳实践等方面进行探讨,帮助读者更好地理解和应用Go语言的断行规则
    2023-10-10
  • golang实现java uuid的序列化方法

    golang实现java uuid的序列化方法

    这篇文章主要介绍了golang实现java uuid的序列化方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09

最新评论