Go第二课-Go入口和目录
Go源文件
- Go 源文件总是用全小写字母形式的短小单词命名,并且以.go 扩展名结尾。
- 直接是将多个单词连接起来作为源文件名,而不是使用其他分隔符,比如下划线。
- 我们尽量不要用两个以上的单词组合作为文件名,否则就很难分辨了。
Go入口函数-main
-
Go 语言中有一个特殊的函数:main 包中的 main 函数,也就是
main.main,它是所有 Go 可执行程序的用户层执行逻辑的入口函数。 -
包是 Go 语言的基本组成单元,通常使用单个的小写单词命名。
-
整个 Go 程序中仅允许存在一个名为 main 的包,入口 package 必须为
package main。可执行程序的 main 包必须定义 main 函数,入口函数必须为func main(){},否则 Go 编译器会报错返回runtime.main_main·f: function main is undeclared in the main package。 -
在启动了多个 Goroutine(Go 语言的轻量级用户线程,后面我们会详细讲解)的 Go 应用中,
main.main函数将在 Go 应用的主 Goroutine 中执行。 -
但按照 Go 的可见性规则(小写字母开头的标识符为非导出标识符),非 main 包中自定义的 main 函数仅限于包内使用。
-
通常导入路径的最后一个分段名与包名是相同的。
-
只有首字母为大写的标识符才是导出的(Exported),才能对包外的代码可见;如果首字母是小写的,那么就说明这个标识符仅限于在声明它的包内可见。
-
Go 源码文件本身采用的是 Unicode 字符集,而且用的是 UTF-8 标准的字符编码方式。
-
Go 中 main函数 不支持返回值,使用
os.exit()作为返回值,否则会返回too many return values have (number) want ()package main import ( "fmt" "os" ) func main() { fmt.Println("Hello World!") os.Exit(10) } -
Go中的 main 函数 不支持传参,使用 os.Args 作为传参
func main() { fmt.Println(os.Args[1]) fmt.Println("Hello World!") os.Exit(0) }
Go入口函数-init
-
init 函数是一个无参数无返回值的函数。
-
“main 函数不一定是用户层第一个被执行的函数”是因为如果 main 包依赖的包中定义了 init 函数,或者是 main 包自身定义了 init 函数,那么 Go 程序在这个包初始化的时候,就会自动调用它的 init 函数,因此这些 init 函数的执行就都会发生在 main 函数之前。
-
不能手工显式地调用 init,否则就会收到编译错误。
-
在初始化 Go 包时,Go 会按照一定的次序,逐一、顺序地调用这个包的 init 函数。一般来说,先传递给 Go 编译器的源文件中的 init 函数,会先被执行;而同一个源文件中的多个 init 函数,会按声明顺序依次执行。
-
Go 在进行包初始化的过程中,会采用“深度优先”的原则,递归初始化各个包的依赖包。
-
包中按照“常量 -> 变量 -> init 函数”的顺序先对包进行初始化。
-
包内的多个 init 函数按出现次序进行自动调用。
-
一个被多个包依赖的包仅会初始化一次。
-
init 函数作用:
- 重置包级变量值。
- 是实现对包级变量的复杂初始化。
- 在 init 函数中实现“注册模式”。
Go 目录
-
对于以生产可执行程序为目的的 Go 项目,它的典型项目结构分为五部分:
- 放在项目顶层的 Go Module 相关文件,包括 go.mod 和 go.sum;
- cmd 目录:存放项目要编译构建的可执行文件所对应的 main 包的源码文件;
- 项目包目录:每个项目下的非 main 包都“平铺”在项目的根目录下,每个目录对应一个 Go 包;
- internal 目录:存放仅项目内部引用的 Go 包,这些包无法被项目之外引用;
- vendor 目录:这是一个可选目录,为了兼容 Go 1.5 引入的 vendor 构建模式而存在的。这个目录下的内容均由 Go 命令自动维护,不需要开发者手工干预。
-
对于以生产可复用库为目的的 Go 项目,它的典型结构则要简单许多,可以直接理解为在 Go 可执行程序项目的基础上去掉 cmd 目录和 vendor 目录。