探索 Go 中的语法糖:综合指南
The free man is he who does not fear to go to the end of his thought.
— Léon Blum
Golang 语法糖简介:
解释什么是语法糖以及它们在 Golang 中的重要性。
在计算机科学中,”语法糖”是指一种编程语法,它是为了使程序更易于读或表达,而在内部转化为基础语法。换句话说,这种语法并没有引入新的功能,而是提供了一种更加方便的编程方式。
Golang,作为一个现代语言,包含了大量的语法糖来减轻程序员的负担并使代码更可读。
例如,许多高级编程语言中的 ++
操作符,它是语法糖,用于增加变量的值 1。因此,我们可以输入 i++
而不是 i = i + 1
,它更短和更快速地输入,并且它表达相同的增量操作。
接下来我们将会遍历Golang中的常见语法糖,分别详细介绍。这将会帮助你更好的理解和使用Golang,写出更紧凑、可读性更高的代码。
当然,学习的目标不仅是学习这些特性,更重要的是了解何时和为什么要使用它们。一个负责的 Go 开发人员不仅知道如何利用这些语法糖,还需要知道何时合适地使用它们。
可变长参数
基本介绍
Go
语言允许一个函数把任意数量的值作为参数,Go
语言内置了…操作符,在函数的最后一个形参才能使用,使用它必须注意如下事项:
- 一个函数的最后一个参数可以是一个变长参数;
- 一个函数可以最多有一个变长参数;
- 一个变长参数的类型总为一个切片类型。
声明和调用
变长函数声明和普通函数声明类似,只不过最后一个参数必须为变长参数。 一个变长参数在函数体内将被视为一个切片。
1 | func SumData(values ...int64) (sum int64) { |
在变长参数函数调用中,可以使用两种风格的方式将实参传递给类型为[]T
的变长形参:
1.传递一个切片做为实参。此切片必须可以被赋值给类型为
[]T
的值(或者说此切片可以被隐式转换为类型[]T
)。 **此实参切片后必须跟随三个点…
**。
2.传递零个或者多个可以被隐式转换为T
的实参(或者说这些实参可以赋值给类型为T
的值)。 这些实参将被添加入一个匿名的在运行时刻创建的类型为[]T
的切片中,然后此切片将被传递给此函数调用。
注意,这两种风格的方式不可在同一个变长参数函数调用中混用。
1 | func main() { |
fmt
标准库包中的Print
、Println
和Printf
函数均为变长参数函数。 它们的声明大致如下:
1 | func Print(a ...interface{}) (n int, err error) |
忽略相关信息
我们只想初始化包里的init
函数,但是不会使用包内的任何方法,这时就可以使用 _ 操作符号重命名导入一个不使用的包:
1 | import _ "github.com/tfrain" |
方法的返回值我们并不一定都使用,还要绞尽脑汁的给他想一个命名,有没有办法可以不处理不要的返回值呢?当然有,还是 _ 操作符,将不需要的值赋给空标识符,就可以忽略:
1 | _, ok := test(a, b int) |
- 有些时候我们想要
json
里面的某些字段不参加序列化,- 操作符可以帮我们处理,Go
语言的结构体提供标签功能,在结构体标签中使用 - 操作符就可以对不需要序列化的字段做特殊处理: - 我们使用
json.Marshal
进行序列化时不会忽略struct
中的空值,默认输出字段的类型零值(string
类型零值是””,对象类型的零值是nil
),如果我们想在序列化时忽略掉这些没有值的字段时,可以在结构体标签中中添加omitempty
:
1 | type Person struct{ |
声明相关
短变量声明
每次使用变量时都要先进行函数声明,对于些其他语言的人来说,并不习惯,那么在Go
语言是不是也可以不进行变量声明直接使用呢?我们可以使用 name := expression 的语法形式来声明和初始化局部变量,相比于使用var
声明的方式可以减少声明的步骤:
1 | var a int = 10 |
使用短变量声明时有两个注释事项:
- 短变量声明只能在函数内使用,不能用于初始化全局变量
- 短变量声明代表引入一个新的变量,不能在同一作用域重复声明变量
- 多变量声明中如果其中一个变量是新变量,那么可以使用短变量声明,否则不可重复声明变量;
声明不定长数组
数组一般是有固定长度的,所以我们在声明数组时一般要声明长度,因为数组在编译时就要确认,但也可以不写数组长度,使用…操作符声明数组时,你只管填充元素值,其他的交给编译器自己去搞就好了;
a := […]int{1, 3, 5} // same as a := [3]{1, 3, 5}
有时我们想声明一个大数组,但是某些index
想设置特别的值也可以使用…操作符搞定:
a := […]int{1: 20, 999: 10} // 数组长度是100, 下标1的元素值是20,下标999的元素值是10,其他元素值都是0
判断相关
判断map的key是否存在
Go语言提供语法 value, ok := m[key]
来判断map
中的key
是否存在,一般都是只利用ok来进行判断。value如果存在就会返回key所对应的值,不存在就会返回空值:
1 | import "fmt" |
类型断言
我们通常都会使用interface
,一种是带方法的interface
,一种是空的interface
,Go1.18
之前是没有泛型的,所以我们可以用空的interface{}
来作为一种伪泛型使用,当我们使用到空的interface{}
作为入参或返回值时,就会使用到类型断言,来获取我们所需要的类型,在Go语言中类型断言的语法格式如下:
1 | value, ok := x.(T) |
x是interface
类型,T是具体的类型。这里类型断言需要区分x
的类型,
如果x
是空接口类型:
空接口类型断言实质是将eface中_type与要匹配的类型进行对比,匹配成功在内存中组装返回值,匹配失败直接清空寄存器,返回默认值。
如果x
是非空接口类型:
*非空接口类型断言的实质是 iface 中 itab 的对比。itab 匹配成功会在内存中组装返回值。匹配失败直接清空寄存器,返回默认值。*
English post: https://programmerscareer.com/golang-syntactic-sugar/
作者:Wesley Wei – Twitter Wesley Wei – Medium
注意:本文为作者原创,转载请注明出处。
评论