
在 go 语言中,若要在包外部使用结构体类型(如实现接口),必须将类型名及字段名首字母大写以导出;否则编译器会报“cannot refer to unexported name”错误。
在 go 语言中,若要在包外部使用结构体类型(如实现接口),必须将类型名及字段名首字母大写以导出;否则编译器会报“cannot refer to unexported name”错误。
Go 的可见性规则基于标识符的首字母大小写:首字母大写的标识符(如 TxtScanner、Txt)是导出的(public),可在其他包中访问;首字母小写的(如 txtScanner、txt)是未导出的(private),仅限本包内使用。这正是你遇到编译错误的根本原因——poke.txtScanner 和其字段 txt 均未导出,导致 main 包无法引用。
要正确实现 Scanner 接口并在 main 包中使用,需遵循以下规范:
✅ 1. 导出结构体类型与字段
将 txtScanner 改为 TxtScanner,字段 txt 改为 Txt(或其他有意义的大写字段名):
// txt_scanner.go
package poke
type TxtScanner struct {
Txt string // 导出字段,首字母大写
}
func (ts TxtScanner) Scan() *ScanResult {
// 实际扫描逻辑
result := ScanResult{Message: "Scanned via TxtScanner"}
return &result
}✅ 2. 提供导出的构造函数(推荐)
虽然可直接字面量初始化(如 poke.TxtScanner{"example"}),但更符合 Go 惯例的方式是提供一个导出的构造函数(首字母大写):
// txt_scanner.go(补充)
func NewTxtScanner(txt string) Scanner {
return TxtScanner{Txt: txt}
}✅ 3. 在 main 中正确使用
导入包后,通过导出的类型或构造函数创建实例,并赋值给接口变量:
// cmd/poke.go
package main
import "github.com/achanda/poke"
func main() {
// 方式一:直接使用导出结构体字面量
var scanner poke.Scanner = poke.TxtScanner{Txt: "hello"}
// 方式二(推荐):使用导出的构造函数
// scanner := poke.NewTxtScanner("hello")
result := scanner.Scan()
_ = result // 使用 result,避免编译警告
}⚠️ 注意事项:
- 接口 Scanner 本身已导出(type Scanner 首字母大写),无需额外操作;
- ScanResult 类型也必须导出(如 type ScanResult struct {...}),否则 *ScanResult 返回类型在包外不可用;
- 方法接收者类型(如 TxtScanner)必须导出,才能让该方法成为导出接口的合法实现;
- 不要试图在 main 包中直接调用未导出的 newTxtScanner()(小写开头),它对外不可见——应改用 NewTxtScanner。
总结:Go 的封装机制严格依赖命名约定。实现接口不是语法问题,而是可见性问题。只要确保接口、实现类型、关键字段、构造函数和返回类型全部导出,即可无缝跨包使用接口抽象,实现松耦合设计。

















