跳到主要内容

Import / Export(导入与导出)

Go 使用 import 引用其他包,通过标识符首字符的大小写决定名称能否被其他包访问。它没有 publicprivateexport 关键字;可见性的基本边界是包,而不是源文件。

导入并使用包

多个导入通常写成分组形式:

package main

import (
"fmt"
"strings"
)

func main() {
message := strings.ToUpper("hello, go")
fmt.Println(message)
}

运行后输出:

HELLO, GO

导入路径定位包,代码默认使用目标包在 package 子句中声明的名称。例如导入 "encoding/json" 后通常通过 json.Marshal 调用函数。

导出规则

一个名称满足以下条件时才是已导出的(exported):

  1. 名称的首字符是 Unicode 大写字母。
  2. 名称声明在包块中,或者它是字段名或方法名。
package profile

const DefaultRole = "reader" // 可被其他包访问
const maxNameBytes = 64 // 仅当前包可访问

type User struct {
Name string // 导出字段
password string // 未导出字段
}

func NewUser(name string) User { // 导出函数
return User{Name: name}
}

func normalizeName(name string) string { // 未导出函数
return name
}

UserNameNewUser 可以从其他包访问,maxNameBytespasswordnormalizeName 只能在 profile 包内部使用。同一个包即使分散在多个文件中,也可以访问彼此未导出的包级名称。

大写表示可访问,不等于 API 已经设计完善。导出的名称会扩大兼容性和维护范围,应只公开调用方真正需要的能力。

导入名称与别名

当包名冲突或默认名称不够清晰时,可以声明别名:

package main

import (
"fmt"

cryptorand "crypto/rand"
mathrand "math/rand"
)

func main() {
buffer := make([]byte, 4)
_, err := cryptorand.Read(buffer)
if err != nil {
fmt.Println("read random bytes:", err)
return
}

fmt.Println("secure bytes:", buffer)
fmt.Println("pseudo-random number:", mathrand.Intn(10))
}

别名只在当前文件生效。不要为每个导入随意起别名;默认包名通常更容易被 Go 开发者识别。

特殊导入形式

空白导入

使用空白标识符 _ 导入包,表示只需要该包的初始化副作用:

import _ "example.com/database-driver"

这种形式常见于数据库驱动或插件注册。它隐藏了直接调用关系,应在确有注册机制需要时使用,并通过注释或包文档说明副作用。

点导入

点导入会让目标包的已导出名称直接进入当前文件作用域:

import . "fmt"

此后可以直接写 Println,但名称来源变得不清晰,也更容易冲突。除少数测试或框架约定外,普通业务代码应避免点导入。

internal

目录名 internal 提供额外的导入限制。位于 internal 目录下的包,只能被以该 internal 父目录为根的目录树中的代码导入:

example.com/shop/
├── internal/token/
├── cmd/api/
└── tools/

这里 cmd/apitools 可以导入 example.com/shop/internal/token,模块目录树之外的代码不可以。这是 Go 工具链实施的导入边界,与名称是否大写无关。

文件级导入与未使用检查

导入声明的作用域仅覆盖当前文件。下面的 fmt 即使在同包另一个文件中已导入,也不能直接使用:

package report

import "fmt"

func Print(title string) {
fmt.Println(title)
}

每个非空白导入都必须在当前文件中使用,否则编译器会报错。开发过程中不应长期用 _ 伪装未使用导入;应删除无用依赖,让工具在真正需要时重新加入。

与 TypeScript 的区别

对比项GoTypeScript
导入单位模块及其导出成员
导出语法包级名称、字段或方法以大写字母开头使用 export 关键字
私有边界未导出名称可被同包所有文件访问未导出名称通常只在当前模块内访问
导入检查当前文件的普通导入必须使用取决于编译器和 lint 配置

Go 的大小写规则既影响命名,也定义公开 API。重命名首字母可能直接改变外部调用方能否编译。

依赖版本、模块路径和 go.mod 的完整规则见 Module Manager(模块管理)

常见错误

  • 导入完整路径后,仍用路径最后一段猜测包名;应以目标包声明为准。
  • 试图从其他包访问小写开头的名称;未导出名称没有跨包访问入口。
  • 为解决未使用导入而改成 _;空白导入只用于明确需要初始化副作用的场景。
  • 大量使用点导入,导致读者无法判断名称来源。
  • 认为导出字段仍然是“只读”的;大写字段通常可被其他包直接读取和修改。

小结

  • import 引用包,多个导入通常使用分组写法。
  • 包级名称、字段和方法以大写字母开头时可导出,小写开头时仅包内可见。
  • 导入别名用于解决真实冲突或提高辨识度,且只在当前文件有效。
  • _ 导入只保留初始化副作用,点导入应谨慎使用。
  • internal 目录可以在名称可见性之外进一步限制包的导入范围。