句法糖(英文:Syntacticsugar)是英国计算机科学家PeterLanding发明的一个术语,指的是在计算机语言中加入的某种语法,对语言的功能没有任何影响,但更方便程序员使用。语法糖可以让程序更简洁,更易读。具体到Go语言中,常见的语法糖有哪些?本文将盘点一下。1、简短的变量声明在Go函数中,我们可以使用name:=表达式的语法形式来声明和初始化局部变量。这个语法糖的功能是声明(类型推断)和赋值。例如x:=1等价于以下形式//形式1varxintx=1//形式2varxint=1//形式3varx=1function:=,因为在任何函数之外,语句都应该以关键字开头,例如type、var等。//Illegalx:=42//Legalvary=42funcmain(){//Legalz:=42}:=代表引入了一个新的变量,所以同一个:=语句不能在同一个作用域中使用两次。x:=1x:=1//重复定义,错误在多变量声明中,如果其中一个变量是新的,使用:=两次x,y:=1,2y,z:=3,4//z是新的variablex,z:=5,6//错误,x,z都已定义您可以使用:=在新范围内声明变量,即使该变量之前已使用相同名称声明过。varxint=1funcsome(){x:=2...}可以在一个简短的语句块中声明相同的名称,例如:if、for、switch,但它们有自己的作用域。funcmain(){x:=1如果为真{x:=2fmt.Printf("x=%d\n",x)//x=2}fmt.Printf("x=%d\n",x)//x=1}所以,如果你想轻松地声明一个变量,你可以使用:=;但是如果你只是想覆盖一个现有的变量,你应该使用=。2.new函数Go内置的new函数是另一种创建变量的方式。表达式new(T)创建一个T类型的未命名变量,将其初始化为T类型的零值,并返回其地址(类型为*T)。比如下面两个newInt函数是等价的funcnewInt()*int{returnnew(int)}funcnewInt()*int{varxintreturn&x}显然,new函数也是为了方便程序员使用而设计的的。3....和切片在Go的函数定义中,我们可以使用...来表示可变参数,它可以接受任意数量的相同类型的参数。最经典的例子就是fmt包中的Println函数funcPrintln(a...interface{})(nint,errerror){}...T语法糖,本质上代表了一个元素类型为T的切片。因此,...interface{}类型等同于[]interface{},这就是为什么Println函数可以接受任意数量的任意类型的参数。Println函数可以称为可变参数函数。可变参数函数具有以下特点。可变参数必须在函数参数列表的最后定义,并且只能有一个可变参数类型定义。调用函数时,可变参数可以留空,在函数内部会被当作一个nilslice。可变参数必须是同一类型,如果需要不同类型定义接口{}。...也用于切片初始化。想一想,如果让你初始化一个intslice,除了第50位为1,第99位为2,其余位全为0,你会怎么定义呢?如果你使用...语法糖,我们可以这样做x:=[...]int{49:1,98:2,99:0}4.接收者方法在Go中,对于一个自定义类型T,为其定义方法时,接收者可以是类型T本身,也可以是类型T的指针*T。typeInstancestruct{}func(ins*Instance)Foo()string{return""}在上面的例子中,当我们定义Instance的Foo方法时,它的接收者是一个指针类型(*Instance)。funcmain(){var_=Instance{}.Foo()//编译错误:无法调用Instance{}上的指针方法}因此,如果我们用Instance类型本身的Instance{}值调用Foo方法,我们会得到上面的错误。typeInstancestruct{}func(insInstance)Foo()string{return""}funcmain(){var_=Instance{}.Foo()//Compiled}此时,如果我们设置Foo的receivermethod改成Instance类型,就没有问题了。这说明在定义类型T的函数方法时,它的接收者类型决定了后面什么类型的对象可以调用该函数方法。但是,真的是这样吗?typeInstancestruct{}func(ins*Instance)String()string{return""}funcmain(){varinsInstance_=ins.String()}实际上,即使我们实现了Foo方法,接收者也是一个指针类型,使用上面的ins调用还是没问题的。Ins值属于Instance类型,不是*Instance,但是可以调用Foo方法。为什么?这其实就是Go编译器提供的语法糖!当一个变量是可变的时,我们直接在类型为T的变量上调用*T方法是合法的,因为Go编译器隐式地获取了它的地址。可变意味着变量是可寻址的,所以上面提到的Instance{}.Foo()会得到一个编译错误,因为Instance{}的值是不可寻址的。5.forrange循环是所有编程语言都会涉及的一个控制单元,最经典的就是三段循环。因为我:=0;我<长度(arr);i++{}每次写三段很麻烦?因此,在Go中,我们可以使用forrange来快速遍历可迭代对象,比如数组和切片、map、channel、string等。对于切片、数组、字符串,范围遍历方法一共有三种a:=[]int{1,2,3}//遍历一:不关心索引和数据forrangea{}//遍历二:只关心索引forindex:=rangea{fmt.Println(index)}//遍历三:关心索引和数据forindex,value:=rangea{fmt.Println(index,value)}map还有三种用于范围遍历的方法m:=map[int]string{1:"Golang",2:"Python",3:"Java"}//遍历1:Don'tcareaboutkeyandvalueforrangem{}//遍历2:只关心keyforkey:=rangem{fmt.Println(key)}//遍历2:关心key和valueforkey,value:=rangem{fmt.Println(key,value)}对于channel,rangech有两种遍历方式:=make(chanint,10)//遍历一:不关心rangech的通道数据{}//遍历二:关心通道数据的data:=rangech{fmt.Println(data)}Go编译器会将不同的forrange遍历方法转换成不同的控制逻辑,简化使用逻辑,方便程序员迭代对象遍历处理。总结句法糖可以让程序员用更简洁的语言来表达更复杂的意思。其实质是编译器做了额外的处理逻辑。本文列举了一些Go的语法糖规则。童鞋们之前了解过吗?如有遗漏欢迎补充~
