GO开发指南
本文最后更新于:2023年6月19日 晚上
记录在进行 go 语言开发遇到的各种问题。
命名规范
文件命名
文件命名一律采用小写,不用驼峰式,尽量见名思义,看见文件名就可以知道这个文件下的大概内容。
其中测试文件以test.go 结尾,除测试文件外,命名不出现。
例子:
stringutil.go, stringutil_test.go
package
包名用小写,使用短命名,尽量和标准库不要冲突。
包名统一使用单数形式。
在 go 源文件的开头必须申明文件所属的 package,如下所示:
1 |
|
命名规范
- 建议 package 命名用小写字母
- 建议 package 命名必和其路径的最后一段一致(main package 除外)。注意,这并不是 Golang 的强制要求,文件目录只是用于存放同一个 package 的所有源文件,Golang 对目录名并无要求。但一个目录下不允许有多个 package 的源文件。
- main package 中的 main 方法是可执行文件的入口,main package 名一般和路径名不一致
- 不同路径下 package 命名可以重复,但其完整路径名必须唯一
变量
变量命名一般采用驼峰式,当遇到特有名词(缩写或简称,如 DNS)的时候,特有名词根据是否私有全部大写或小写。
例子:
apiClient、URLString
接口
单个函数的接口名以 er 为后缀
1 |
|
两个函数的接口名综合两个函数名,如:
1 |
|
三个以上函数的接口名类似于结构体名,如:
1 |
|
方法
方法名应该是动词或动词短语,采用驼峰式。将功能及必要的参数体现在名字中, 不要嫌长, 如 updateById,getUserInfo.
如果是结构体方法,那么 Receiver 的名称应该缩写,一般使用一个或者两个字符作为 Receiver 的名称。如果 Receiver 是指针, 那么统一使用 p。 如:
1 |
|
1 |
|
对于 Receiver 命名应该统一, 要么都使用值, 要么都用指针。
每个以大写字母开头(即可以导出)的方法应该有注释,且以该函数名开头。如:
1 |
|
大写字母开头的方法以为着是可供调用的公共方法,如果你的方法想只在本包内掉用,请以小写字母开发。如:
1 |
|
注释应该用一个完整的句子,注释的第一个单词应该是要注释的指示符,以便在 godoc 中容易查找。
注释应该以一个句点 . 结束。
各种报错
【1】“与你运行的 Windows 版本不兼容“的解决方法
https://blog.csdn.net/willingtolove/article/details/107924423
其实就是 package 名字错了,必须改成 main,不清楚为什么?
【2】每次创建新的项目都要重新设置一个 configuration 文件,后发现是我的问题,开发的时候,可以把所有的项目都在 goland 里打开,根目录就是$GOPATH,像这样:
所有小项目都放在 src 里,是不是很棒?
但是!必须有 main 包,必须有 main 函数作为程序入口!
具体原理看:
https://blog.csdn.net/zxy_666/article/details/80390843
如何写出优雅的 Golang 代码
Golang import 包问题相关详解
理解 Go 语言包(package)
【3】结构体属性导出问题
结构体中的属性,如果是小写开头,是无法被跨包使用的,也就是说包外无法访问,太狗了。
Go Struct 超详细讲解 建议好好看看这篇
函数知识
make
make()函数在 golang 的代码如下:func make(t Type,size IntegerType) Type
使用 make 来创建 slice,map,chanel 说明如下:
1 |
|
在创建 slice 时第一个参数用于确定初始化该 slice 的大小该 slice 中的值为零值,第三个参数用于确定该 slice 的长度
map:
1 |
|
根据 size 大小来初始化分配内存,不过分配后的 map 长度为 0,如果 size 被忽略了,那么会在初始化分配内存时分配一个小尺寸的内存
nil
相信写过 Golang 的程序员对下面一段代码是非常非常熟悉的了:
1 |
|
当出现不等于nil
的时候,说明出现某些错误了,需要我们对这个错误进行一些处理,而如果等于nil
说明运行正常。那什么是nil
呢?查一下词典可以知道,nil
的意思是无,或者是零值。零值,zero value,是不是有点熟悉?在 Go 语言中,如果你声明了一个变量但是没有对它进行赋值操作,那么这个变量就会有一个类型的默认零值。这是每种类型对应的零值:
1 |
|
举个例子,当你定义了一个 struct:
1 |
|
变量p
只声明但没有赋值,所以 p 的所有字段都有对应的零值。那么,这个nil
到底是什么呢?Go 的文档中说到,_nil 是预定义的标识符,代表指针、通道、函数、接口、映射或切片的零值_,也就是预定义好的一个变量:
slices
1 |
|
一个为nil
的 slice,除了不能索引外,其他的操作都是可以的,当你需要填充值的时候可以使用append
函数,slice 会自动进行扩充。那么为nil
的 slice 的底层结构是怎样的呢?根据官方的文档,slice 有三个元素,分别是长度、容量、指向数组的指针:
当有元素的时候,
channel
Channel 是 Go 中的一个核心类型,你可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。
它的操作符是箭头 <- 。
1 |
|
(箭头的指向就是数据的流向)
就像 map 和 slice 数据类型一样, channel 必须先创建再使用:
1 |
|
定义与用法
它包括三种类型的定义。可选的<-
代表 channel 的方向。如果没有指定方向,那么 Channel 就是双向的,既可以接收数据,也可以发送数据。
1 |
|
使用make
初始化 Channel,并且可以设置容量:
1 |
|
容量(capacity)代表 Channel 容纳的最多的元素的数量,代表 Channel 的缓存的大小。
如果没有设置容量,或者容量设置为 0, 说明 Channel 没有缓存,只有 sender 和 receiver 都准备好了后它们的通讯(communication)才会发生(Blocking)。如果设置了缓存,就有可能不发生阻塞, 只有 buffer 满了后 send 才会阻塞, 而只有缓存空了后 receive 才会阻塞。一个 nil channel 不会通信。
可以通过内建的close
方法可以关闭 Channel。
你可以在多个 goroutine 从/往 一个 channel 中 receive/send 数据, 不必考虑额外的同步措施。
Channel 可以作为一个先入先出(FIFO)的队列,接收的数据和发送的数据的顺序是一致的。
channel 的 receive 支持 multi-valued assignment,如
1 |
|
它可以用来检查 Channel 是否已经被关闭了。
send
send 语句用来往 Channel 中发送数据, 如ch <- 3
。
它的定义如下:
1 |
|
在通讯(communication)开始前 channel 和 expression 必选先求值出来(evaluated),比如下面的(3+4)先计算出 7 然后再发送给 channel。
1 |
|
send 被执行前(proceed)通讯(communication)一直被阻塞着。如前所言,无缓存的 channel 只有在 receiver 准备好后 send 才被执行。如果有缓存,并且缓存未满,则 send 会被执行。
往一个已经被 close 的 channel 中继续发送数据会导致run-time panic。
往 nil channel 中发送数据会一致被阻塞着。
receive
<-ch
用来从 channel ch 中接收数据,这个表达式会一直被 block,直到有数据可以接收。
从一个 nil channel 中接收数据会一直被 block。
从一个被 close 的 channel 中接收数据不会被阻塞,而是立即返回,接收完已发送的数据后会返回元素类型的零值(zero value)。
如前所述,你可以使用一个额外的返回参数来检查 channel 是否关闭。
阻塞
缺省情况下,发送和接收会一直阻塞着,直到另一方准备好。这种方式可以用来在 gororutine 中进行同步,而不必使用显示的锁或者条件变量。
如官方的例子中x, y := <-c, <-c
这句会一直等待计算结果发送到 channel 中。
1 |
|
Range
for …… range
语句可以处理 Channel。
1 |
|
range c
产生的迭代值为 Channel 中发送的值,它会一直迭代直到 channel 被关闭。上面的例子中如果把close(c)
注释掉,程序会一直阻塞在for …… range
那一行。
defer
https://tiancaiamao.gitbooks.io/go-internals/content/zh/03.4.html
这个博客讲的挺好的,其他知识也有,可以常看看。
另外还有:https://sanyuesha.com/2017/07/23/go-defer/
其他知识
goroutine
https://www.cnblogs.com/wdliu/p/9272220.html 调度原理
代码示例:
1 |
|
goroutine 异常捕捉
当启动多个 goroutine 时,如果其中一个 goroutine 异常了,并且我们并没有对进行异常处理,那么整个程序都会终止,所以我们在编写程序时候最好每个 goroutine 所运行的函数都做异常处理,异常处理采用 recover.
1 |
|
方法
Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。语法格式如下:
1 |
|
例如:
1 |
|
函数传参
数组和切片很像,但是在调用的时候他们传递的类型是不同的!
- Go 语言的数组是值,其长度是其类型的一部分,作为函数参数时,是 值传递,函数中的修改对调用者不可见
- Go 语言中对数组的处理,一般采用 切片 的方式,切片包含对底层数组内容的引用,作为函数参数时,类似于 指针传递,函数中的修改对调用者可见。
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!