写点什么

Go 语言入门基础之库源码文件

作者:Xiao8
  • 2022 年 6 月 15 日
  • 本文字数:2103 字

    阅读完需:约 7 分钟

在本场 Chat 中,会讲到如下内容:


  • 什么是使用库源码文件

  • 拆分命令源码文件

  • 代码包的导入路径总会与其所在目录的相对路径

  • 什么样的程序实体才可以被当前包外的代码引用


概念:何为库源码文件


不具备命令源码文件的声明自己属于 main 包、包含无参数声明、结果声明的 main 参数的特征的源码文件,即为库源码文件。


注意:库源码文件不能被直接运行,它仅用于存放程序实体。只要遵从 Go 语言规范,这些程序实体就可以被其他代码使用。这些“其他代码”可以与被使用的程序实体在同一个源码文件内,也可以在其他源码文件,甚至其他代码包中。


程序实体是什么?


在 Go 语言中,它是变量、常量、函数、结构体和接口的统称。我们总是会先声明(或者说定义)程序实体,然后再去使用。比如之前的例子中,我们先定义了变量 testStr,然后在 main 函数中调用 fmt.Printf 函数的时候用到了它。


程序实体的名字被统称为标识符。标识符可以是任何 Unicode 编码可以表示的字母字符、数字以及下划线“_”,但是其首字母不能是数字。和 java 一样。


问题:怎样把命令源码文件中的代码拆分到其他源码文件?


我们下面的 demo 分成如下:


#demo.go


package main

import ( "flag")

var testStr string

func init(){ flag.StringVar(&testStr, "testStr", "小码哥Damon", "介绍说明")}

func main() { //用于真正解析参数 flag.Parse() //fmt.Printf("Hello, %s!\n", testStr) test(testStr)}
复制代码


#demo_lib.go


【1】

import "fmt"

func hello(name string) { fmt.Printf("Hello, %s!\n", name)}
复制代码


那么【1】该怎么写呢?


显然:写入


package main
复制代码


然后执行:


go run demo.go demo_lib.goHello, 小码哥Damon!
复制代码


这里,我把上面两个文件放在一个目录下。


注意,demo.go 和 demo_lib.go 都声明自己属于 main 包。这种用法,即:源码文件声明的包名可以与其所在目录的名称不同,只要这些文件声明的包名一致就可以。


之前说过,需要把项目代码的根目录加入到环境变量 GOPATH 中,这样使其目录成为工作区之一。


问题解析


这个问题考察的是代码包声明的基本规则。这里总结一下:


第一条规则,同目录下的源码文件的代码包声明语句要一致。也就是说,它们要同属于一个代码包。这对于所有源码文件都是适用的。


如果目录中有命令源码文件,那么其他种类的源码文件也应该声明属于 main 包。这也是我们能够成功构建和运行它们的前提。


第二条规则,源码文件声明的代码包的名称可以与其所在的目录名不同。在针对代码包进行构建时,生成的结果文件的主名称与其父目录名一致。


对于命令源码文件而言,构建生成的可执行文件的主名称会与其父目录名相同。


问题:怎样把命令源码文件中的代码拆分到其他代码包?


我们把代码 demo.go 文件放在了一个叫 src 目录下的 demo2 目录下,然后在 demo2 目录下创建一个 lib 目录,再把 demo_lib.go 放到该目录下。


#src/demo2


package main

import ( "flag" "demo2/lib")

var name string

func init() { flag.StringVar(&name, "name", "everyone", "The greeting object.")}

func main() { flag.Parse() lib.Hello(name)}
复制代码


#src/demo2/lib


package lib3

import "fmt"

func Hello(name string) { fmt.Printf("Hello, %s!\n", name)}
复制代码


这里可以看到改变的是:第一个改动是,我把代码包声明语句由 package main 改为了 package lib3。第二个改动是,我把全小写的函数名 hello 改为首字母大写的 Hello。


基于这点,我们再看问题:代码包的导入路径总会与其所在目录的相对路径一致吗?


库源码文件 demo_lib.go 所在目录的相对路径是 demo2/lib,而它却声明自己属于 lib3 包。在这种情况下,该包的导入路径是 demo2/lib,还是 demo2/lib3?


如果是这样会报错:为什么会是这样?


根本原因就是,我们在源码文件中声明所属的代码包与其所在目录名不同。请记住,源码文件所在的目录相对于 src 目录的相对路径就是它的代码包导入路径,而实际使用其程序实体时给定的限定符要与它声明所属的代码包名称对应。


有两个方式可以使上述构建成功完成。我在这里选择把 demo_lib.go 文件中的代码包声明语句改为 package lib。理由是,为了不让该代码包的使用者产生困惑,我们总是应该让声明的包名与其父目录的名称一致。


问题:怎样的程序实体才可以被当前包外的代码引用?


为什么要把 demo_lib.go 文件中的那个函数名称 hello 的首字母大写?实际上这涉及了 Go 语言中对于程序实体访问权限的规则。超级简单,名称的首字母为大写的程序实体才可以被当前包外的代码引用,否则它就只能被当前包内的其他代码引用。通过名称,Go 语言自然地把程序实体的访问权限划分为了包级私有的和公开的。对于包级私有的程序实体,即使你导入了它所在的代码包也无法引用到它。


问题:还有其他的权限规则来访问程序实体吗?


答案是肯定的。在 Go 1.5 及后续版本中,我们可以通过创建 internal 代码包让一些程序实体,仅仅能被当前模块中的其他代码引用。这被称为 Go 程序实体的第三种访问权限:模块级私有。


详细规则:internal 代码包中声明的公开程序实体仅能被该代码包的直接父包及其子包中的代码引用。当然,引用前需要先导入这个 internal 包。对于其他代码包,导入该 internal 包都是非法的,无法通过编译。

发布于: 刚刚阅读数: 4
用户头像

Xiao8

关注

God bless the fighters. 2020.03.11 加入

欢迎关注公众号:程序猿Damon,长期从事Java开发,研究Springcloud的微服务架构设计。目前主要从事基于K8s云原生架构研发的工作,Golang开发,长期研究边缘计算框架KubeEdge、调度框架Volcano、容器云KubeSphere研究

评论

发布
暂无评论
Go语言入门基础之库源码文件_6月月更_Xiao8_InfoQ写作社区