Go 学习笔记
Go 环境搭建
安装 Go。Mac 可以用 brew 安装 Go :
brew install go
。Windows 直接下载安装包安装就行。在命令行下运行go version
命令后能输出版本号表示安装成功。VS Code 安装 Go 插件
环境安装过程中的坑
Go 插件有可能安装失败
解决办法:
1 | // 开启代理设置 |
注意:开启代理设置前,可以使用 go env
命令查看是否已经默认开启了代理设置,如果已经开启就不需要在再次开启了,直接设置代理就行。
package main
语句报错
错误信息:
1 | go: cannot find main module; see 'go help modules' |
以及:
1 | The code in the workspace failed to compile (see the error message below). If you believe this is a mistake, please file an issue: https://github.com/golang/go/issues/new. go [-e -json -compiled=true -test=true -export=false -deps=true -find=false -- ./]: exit status 1: go: cannot find main module; see 'go help modules' : packages.Load error |
上面的问题都是由于 Go 的 Modules 导致的。
解决方法:
- 禁用 Modules(不推荐)。运行
go env -w GO111MODULE=off
- 使用 go.mod。在项目的根目录运行
go mod init main
(其中main
是模块名)
VS Code 没有代码提示
没有代码提示也是因为 go.mod
文件导致的。解决办法也是在项目的根目录运行 go mod init main
(其中 main
是模块名)
基本结构和基本数据类型
文件名、关键词与标识符
- 文件名以
.go
为扩展名,文件名均由小写字母组成,单词间以_
分隔。文件名不包含空格或其他特殊字符。 - Go 语言也是区分大小写的。
- 有效的标识符必须以字符(可以使用任何 UTF-8 编码的字符或
_
)开头。 _
本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。在编码过程中,你可能会遇到没有名称的变量、类型或方法。虽然这不是必须的,但有时候这样做可以极大地增强代码的灵活性,这些变量被统称为匿名变量。- 代码不用显示以
;
结尾,因为 Go 编译器自动完成这项工作。 - 如果你打算将多个语句写在同一行,它们则必须使用
;
人为区分,但在实际开发中我们并不鼓励这种做法。
Go 程序的基本结构和要素
包
示例
1 | package main |
包的路径
- 包名不是以
.
或/
开头,则 Go 会在全局文件进行查找; - 包名以
./
开头,则 Go 会在相对目录中查找; - 如果包名以
/
开头(在 Windows 下也可以这样使用),则会在系统的绝对路径中查找。 - 相同的标识符可以在不同的包中使用,因为可以使用包名来区分它们。
包中标识符的可见性
- 以大写字母开头的标识符(包括常量、变量、类型、函数名、结构字段等等)对象可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);
- 以小写字母开头的标识符对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private)。
- 大写字母可以使用任何 Unicode 编码的字符,比如希腊文,不仅仅是 ASCII 码中的大写字母
注意事项
如果你导入了一个包却没有使用它,则会在构建程序时引发错误,如 imported and not used: os
,这正是遵循了 Go 的格言:“没有不必要的代码!“。
函数
一般形式
1 | func functionName(parameter_list) (return_value_list) { |
其中:
- parameter_list 的形式为 (param1 type1, param2 type2, …)
- return_value_list 的形式为 (ret1 type1, ret2 type2, …)
注意事项
- 左大括号
{
必须与方法的声明放在同一行,这是编译器的强制规定,否则你在使用 gofmt 时就会报错; - 右大括号
}
需要被放在紧接着函数体的下一行。如果你的函数非常简短,也可以整个写成一行。 - Go 语言虽然看起来不使用分号作为语句的结束,但实际上这一过程是由编译器自动完成
函数的命名
- 遵循 Pascal 命名法(所有单词首字母大写)的函数可以被外部包调用;
- 遵循骆驼命名法(第一个单词的首字母小写,其余单词的首字母大写)不能被外部包调用;
main 函数。
main()
函数是每一个可执行程序所必须包含的,否则会引发构建错误;- 一般来说都是在启动后第一个执行的函数(如果有
init ()
函数则会先执行该函数)。 - main 函数既没有参数,也没有返回类型(与 C 家族中的其它语言恰好相反)。如果你不小心为 main 函数添加了参数或者返回类型,将会引发构建错误。
注释
- 注释不会被编译,但可以通过 godoc 来使用
//
单行注释,是最常见的注释形式。/* ... */
多行注释(也叫块注释)。不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
类型
- 使用 var 声明的变量的值会自动初始化为该类型的零值
- 基本类型,如:int、float、bool、string;
- 结构化的(复合的),如:struct、array、slice、map、channel;
- 只描述类型的行为的,如:interface。
- 结构化的类型没有真正的值,它使用 nil 作为默认值
- Go 语言中不存在类型继承
- 多返回值函数:
1
2
3func FunctionName (a typea, b typeb) (t1 type1, t2 type2) {
return var1, var2
} - 使用
type
关键字可以定义你自己的类型 - Go 语言是一种静态类型语言
Go 程序的一般结构
- 在完成包的 import 之后,开始对常量、变量和类型的定义或声明。
- 如果存在 init 函数的话,则对该函数进行定义(这是一个特殊的函数,每个含有该函数的包都会首先执行这个函数)。
- 如果当前包是 main 包,则定义 main 函数。
- 然后定义其余的函数,首先是类型的方法,接着是按照 main 函数中先后调用的顺序来定义相关函数,如果有很多函数,则可以按照字母顺序来进行排序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// gotemplate.go
package main
import (
"fmt"
)
const c = "C"
var v int = 5
type T struct{}
func init() { // initialization of package
}
func main() {
var a int
Func1()
// ...
fmt.Println(a)
}
func (t T) Method1() {
//...
}
func Func1() { // exported function Func1
//...
}
Go 程序执行顺序
Go 程序的执行(程序启动)顺序如下:
- 按顺序导入所有被 main 包引用的其它包,然后在每个包中执行如下流程:
- 如果该包又导入了其它的包,则从第一步开始递归执行,但是每个包只会被导入一次。
- 然后以相反的顺序在每个包中初始化常量和变量,如果该包含有 init 函数的话,则调用该函数。
- 在完成这一切之后,main 也执行同样的过程,最后调用 main 函数开始执行程序。
类型转换
Go 语言不存在隐式类型转换,所有的转换都必须显式说明,就像调用一个函数一样(类型在这里的作用可以看作是一种函数):
1 | valueOfTypeB = typeB(valueOfTypeA) |
Go 命名规范
- 名称不需要指出自己所属的包,因为在调用的时候会使用包名作为限定符
- 返回某个对象的函数或方法的名称一般都是使用名词,没有
GetXXX()
之类的字符,如果是用于修改某个对象,则使用SetXXX()
- 有必须要的话可以使用大小写混合的方式,如
MixedCaps
或mixedCaps
,而不是使用下划线来分割多个名称。