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多值赋值短变量声明的资料请关注脚本之家其它相关文章!

相关文章

  • gin项目部署到服务器并后台启动的步骤

    gin项目部署到服务器并后台启动的步骤

    本文主要介绍了gin项目部署到服务器并后台启动的步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02
  • go HTTP2 的头部压缩算法hpack实现详解

    go HTTP2 的头部压缩算法hpack实现详解

    这篇文章主要为大家介绍了go HTTP2 的头部压缩算法hpack实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • 解析go语言调用约定多返回值实现原理

    解析go语言调用约定多返回值实现原理

    这篇文章主要为大家介绍了解析go语言调用约定多返回值实现原理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • 解读unsafe.Pointer和uintptr的区别

    解读unsafe.Pointer和uintptr的区别

    这篇文章主要介绍了解读unsafe.Pointer和uintptr的区别及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • go语言题解LeetCode453最小操作次数使数组元素相等

    go语言题解LeetCode453最小操作次数使数组元素相等

    这篇文章主要为大家介绍了go语言题解LeetCode453最小操作次数使数组元素相等示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • Golang内存泄漏场景以及解决方案详析

    Golang内存泄漏场景以及解决方案详析

    golang中内存泄露的发现与排查一直是来是go开发者头疼的一件事,下面这篇文章主要给大家介绍了关于Golang内存泄漏场景以及解决的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • 一文详解Golang内存管理之栈空间管理

    一文详解Golang内存管理之栈空间管理

    这篇文章主要介绍了Golang内存管理的栈空间管理,文章通过代码示例介绍的非常详细,对我们学习Golang内存管理有一定的帮助,需要的朋友跟着小编一起来学习吧
    2023-06-06
  • Go实现并发的示例代码

    Go实现并发的示例代码

    Go语言的并发机制是其强大和流行的一个关键特性之一,本文主要介绍了Go实现并发的示例代码,具有一定的参考价值,感兴趣的可以了解一下
    2023-11-11
  • MacOS中 VSCode 安装 GO 插件失败问题的快速解决方法

    MacOS中 VSCode 安装 GO 插件失败问题的快速解决方法

    这篇文章主要介绍了MacOS中 VSCode 安装 GO 插件失败问题的快速解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • 浅谈用Go构建不可变的数据结构的方法

    浅谈用Go构建不可变的数据结构的方法

    这篇文章主要介绍了用Go构建不可变的数据结构的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09

最新评论