在 Go 1.5
及后续版本中,可以通过创建 internal
代码包让一些程序实体仅仅能被当前模块中的其他代码引用。这是第三种访问权限:模块级私有。
1. GOPATH 时代
具体规则是:
internal
代码包中声明的公开程序实体仅能被该代码包的直接父包及其子包中的代码引用。当然,引用前需要先导入这个internal
包。- 对于其他代码包,导入该
internal
包都是非法的,无法通过编译。
这里的名称必须是 internal
。
代码目录结构如下:
wohu@wohu:~/GoCode/src$ tree chapter/
chapter/
└── demo3
├── demo3.go
└── lib
├── demo3_lib.go
└── internal
└── internal.go
3 directories, 3 files
wohu@wohu:~/GoCode/src$
demo3.go
源码如下:
package main
import (
"chapter/demo3/lib"
//in "chapter/demo3/lib/internal" // 此行无法通过编译。
//"os"
)
func main() {
name := "wohu"
lib.Hello(name)
//in.Hello(os.Stdout, name)
}
demo3_lib.go
源码如下:
package lib
import (
in "chapter/demo3/lib/internal"
"os"
)
func Hello(name string) {
in.Hello(os.Stdout, name)
}
internal.go
源码如下:
package internal
import (
"fmt"
"io"
)
func Hello(w io.Writer, name string) {
fmt.Fprintf(w, "Hello, %s!\n", name)
}
运行 demo3.go
结果如下:
[Running] cd "/home/wohu/GoCode/src/chapter/demo3/" && go run .
Hello, wohu!
如果修改 demo3.go
源码如下,试图调用 internal
包,
package main
import (
"chapter/demo3/lib"
in "chapter/demo3/lib/internal" // 此行无法通过编译。
"os"
)
func main() {
name := "wohu"
lib.Hello(name)
in.Hello(os.Stdout, name)
}
模块级私有的 internal
包,仅能被直接父包及其子包中的代码引用。上面如果要在父级里调用孙级目录的 internal
包,则会报错:
demo3.go:5:2: use of internal package chapter/demo3/lib/internal not allowed
2. GOMODULE 时代
一个 Go
项目里的 internal
目录下的 Go
包,只可以被本项目内部的包导入。项目外部是无法导入这个 internal
目录下面的包的。
举个例子,假设我们有两个 go module
,两个 module
的结构如下:
.
├── module1
│ ├── go.mod
│ ├── internal
│ │ └── pkga
│ ├── pkg1
│ └── pkg2
└── module2
├── go.mod
└── pkg1
module1
中的 internal/pkga
包可以被 module1
的 pkg1
和 pkg2
包所导入。但无法被 module2
的pkg1
包所导入。
内部包的规范约定:导出路径包含 internal
关键字的包,只允许 internal
的父级目录及父级目录的子包导入,其它包无法导入。
老师举的例子中,internal
的父级目录是 module1
,父级目录的子包有 module1/pkg1
、module1/pkg2
,所以在这个例子中internal
只能被这三个地方导入。
上面的约定是由 Go
编译器验证的,internal
是 Go
编译器在编译程序时可以识别的特殊目录名,如果验证不过会报错。