golang的奇怪特性
今天跟着golang tour 了解了一下golang,记录一下这门语言的一些奇怪特性
,
(其实就是特性,因为脑子里在酝酿《perl的奇怪特性》这篇文章,所以用相
似的标题)
语言特点
导出名大写
包中导出函数都是大写的,小写的函数不会被导出。用这种简洁的方法
实现了C++中的public与private的作用。
语句末尾没有分号
许多现代语言都已经省略分号了。值得一提的是,javascript也是可以
省略分号的。但有些公司或项目的规范中必须要求有分号,有些则要求必
须省略(vue.js)
类型在变量名之后
代码示例:
为什么这样设计呢?
在这篇文章中有详细解释:Go’s Declaration Syntax
总结起来就是c风格的函数指针太难读了,比如:
这是一个函数的指针,它有两个int型的参数,并且返回int。参数的
名字是可以省略的,只留下类型:
但是,如果函数的参数也是一个函数呢?
这个就很难读了。
因为参数的名字是可省的,那么这个作为参数的函数的名字也可省,这样
这个函数就变成了这样:
WTF 我该把名字补充到哪里去呢?
更加抓狂的例子,如果一个函数的返回值类型是一个函数的指针呢?
这能一眼看出是个函数的声明吗?
C家族以外的语言使用一种完全不同的语法风格:
这比较清楚,但是有点啰嗦,省去冒号就是go的风格:
类似C的main函数,在go中可以这样写:
我们可以从左到右的读它:
“函数main接收一个int型参数和一个string的slice,并且返回int”
把参数名称丢掉,可以一样:
现在,用go写上面c的例子:
定义一个函数,它的参数为函数指针:
返回类型还是个函数
不得不说,这确实比着C风格好读许多
这带来了另外一个好处,我们可以定义一个闭包,然后马上调用它:
这里还隐藏着一个go的思想,区别类型与语句:括号在前,表示类型
括号在后,表示引用,不管是小括号还是中括号。所以,数组类型的
声明和数组元素的引用就分别是这样:
函数的连续几个参数是同一类型,前面的类型可省
|
|
多值返回
比如写个交换函数可以这样方便:
命名返回值
这个有意思,当返回多个值时,可以明确指出多个返回值到底都表示什么意思,可以当文档使用
变量声明
var关键字
不同于c语言家族,声明变量不能只用类型,而是需要一个显示的var关键字,类型放于后面
我觉得这样带来了一个好处:非常方便的区分了变量定义与类型转换。因为在C中这是不容易
分清的
省略类型
如果使用表达式初始化变量,类型可省,由表达式推倒而来
省略var,使用:=符号
在函数内可以使用:=符号,省略var 疑问:不能使用在函数外(why)
常量定义不用var,而用const
|
|
常量不能用:=
疑问:对于惜字如金的go为什么要引入这个符号呢?
go的基本类型
go的基本类型有:
没有了double,而是给了一个float64,这很友好
只有一种循环结构for
不像C,go的循环条件不需要() 但循环体还是需要{}
|
|
循环条件的前后可省,只剩中间
|
|
甚至分号都可省,这就跟while效果一样了
|
|
把所有条件都省略就是死循环
|
|
这个确实比while(true)还要简洁
if的便捷语句
在if的条件之前执行一个简单语句。这个语句中的变量作用域是if范围内
疑问:这个有啥用呢?
switch
switch不需要每一个case后都加个break
在C语言中经常忘加break,既然经常忘加,就干脆省掉
疑问:确实有需要执行多个case的情况该怎样合适的表达呢?
没有条件的switch
|
|
这种写法比一长串if else要简洁易读得多
defer
延迟函数的执行知道上层函数返回
这个好,RAII,比C++的构造析构中的奇技淫巧好用的多。应该是从perl中学来的吧
golang的defer有一个特色:defer后面必须是一个函数调用,不能是表达式
没有指针运算
指针加减什么的在go中是不支持的,疑问:为啥不要呢?
结构体类型关键字type
比c语言的teypedefine更加的直观
通过指针间接访问结构体是透明的
还是用.
,不是用->
数组类型
数组类型是这样表示的:[n]T
slice类型
slice 可变长序列
|
|
这是golang特创的一种类型,在我的认知范围内,其它语言是没有这种类型的。
它实际上提供了一种可变长序列。类似于STL中的vector,perl的arraylen(s)
返回slice s的长度
构造slice
初始化一个slice,需要这样
1s := []int{1,2,3,4,5}这样是不合法的:
1s := [1,2,3,4,5]使用make构建
构建一个长度为5,每一项都为0的数组1a := make([]int,5)构建一个长度为0,但容量为5的数组
1a := make([]int,0,5)
后面的这种方法在js这类语言中是很方便的,但是golang是不允许的
slice的slice
就是二维数组啦
slice的切片
|
|
它是一个半开区间,数学表示是:[lo,hi)
切片的概念perl也有,但是是用lo和len来表示的,相比perl,golang的更符合人类习惯一些
for循环的range格式
类似于perl中的foreach
看来有许多我喜欢的perl的特性哦。这个不知道是不是我自己的感觉,很多语言在设计的
时候都在参考perl–很多人眼中的一门凌乱不堪的语言。es6中抄perl的符号都好几个了,
像$
#
=>
这些
闭包
golang支持闭包,练习题:实现一个fibonacci函数,返回一个闭包可以连续返回的斐波那契数
对一直习惯了C风格代码的我,温习一下闭包:闭包就是给函数绑定一些状态(这个函数内声明的变量),并留待下次调用
接口类型
接口类型是由一组方法定义的集合。在C++中是由类的继承,虚函数等来实现的。把它
作为一个类型更自然。java中也有接口(但我不熟悉java,不知道是不是同样的机制)
接口的赋值运算,接收的是一个实现了这个接口的类型(这样达到了泛型的某些效果)
但这个类型是区分指针类型和直接数据类型的,如果指针类型实现了这个方法,那么直接
类型是不能调用的(疑问:这样设计貌似有道理,但2)
隐式接口解耦了实现接口的包和定义接口的包
类型在实现接口的时候不需要显示声明某个方法是个接口。
Stringer接口
fmt.Println调用了被打印对象的Stringer接口,这个接口的定义是这样的:
这就是说,如果我定义一个结构体,如果实现了一个String函数,Println就可以按我
的要求来打印它:
练习题
独特的error接口
这个好有特色,把error作为一种接口。
在golang中错误是这样处理的:
- 定义一种错误类型
- 这个错误类型实现error接口(也就是Error方法)
- 用fmt.Println打印它的话就会打印出Error方法返回的字符串
没见过别的语言有类似的做法,会不会好用要试试看。
Reader接口
Reader接口的Read方法 用数据填充制定字节slice,并且返回填充的字节数和错误信息。
在遇到数据流结尾时,返回io.EOF错误