pragma.go 文件
文件位于: https://github.com/protocolbuffers/protobuf-go/blob/master/internal/pragma/pragma.go
该文件核心思想: 利用 Golang 语法机制,扩展 Golang 语言特性
目前,该文件提供以下 4 个功能:
功能 | 说明 |
---|---|
NoUnkeyedLiterals | 禁止非 {Key: Value} 方式,初始化结构体 |
DoNotImplement | 禁止第三方扩展实现其接口 |
DoNotCompare | 禁止结构体做比较运算 |
DoNotCopy | 禁止结构体值拷贝 |
下面依次说明实现原理
NoUnkeyedLiterals
定义如下:
// NoUnkeyedLiterals can be embedded in a struct to prevent unkeyed literals.
type NoUnkeyedLiterals struct{
}
使用例子如下(原理见注释):
package main
import (
"fmt"
"unsafe"
)
type NoUnkeyedLiterals struct{
}
type A1 struct {
_ NoUnkeyedLiterals // 不影响 A1 结构体大小
F1 int
F2 string
}
type A2 struct {
F1 int
F2 string
}
func TestNoUnkeyedLiterals() {
_ = A1{
F1: 1, F2: "2"}
_ = A2{
1, "2"} // 如果 A2 增加字段,可能会导致这里的初始化列表错误,而不知
_ = A1{
1, "2"} // error: too few values in struct literal of type A1compilerInvalidStructLit
fmt.Println(unsafe.Sizeof(A1{
}) == unsafe.Sizeof(A2{
})) // true
}
func main() {
TestNoUnkeyedLiterals()
}
DoNotImplement
定义如下:
// DoNotImplement can be embedded in an interface to prevent trivial
// implementations of the interface.
//
// This is useful to prevent unauthorized implementations of an interface
// so that it can be extended in the future for any protobuf language changes.
type DoNotImplement interface{
ProtoInternal(DoNotImplement) }
这个接口定义要结合internal
目录,达成DoNotImplement
接口对外部包不可见
比如protobuf-go
中的FileImports
接口,除了protobuf-go
自己,其他第 3 方是没办法实现FileImports
接口的
看下面代码:
import "google.golang.org/protobuf/internal/pragma"
type doNotImplement pragma.DoNotImplement
// FileImports is a list of file imports.
type FileImports interface {
// Len reports the number of files imported by this proto file.
Len() int
// Get returns the ith FileImport. It panics if out of bounds.
Get(i int) FileImport
doNotImplement
}
因为DoNotImplement
定义在internal
目录下,对第 3 方不可见
FileImports
又需要实现ProtoInternal(DoNotImplement)
方法
因为第 3 方没有办法实现ProtoInternal(DoNotImplement)
,进而阻止了FileImports
接口的第 3 方扩展实现
DoNotCompare
定义如下:
// DoNotCompare can be embedded in a struct to prevent comparability.
type DoNotCompare [0]func()
- 用法和
NoUnkeyedLiterals
一样,内嵌到结构体内 - 同样不占空间,不影响结构体大小(因为定义的是 0 长度数组)
- 因为 func 类型不能比较大小,因此被内嵌的结构体也无法比较大小
例子略
DoNotCopy
定义如下:
// DoNotCopy can be embedded in a struct to help prevent shallow copies.
// This does not rely on a Go language feature, but rather a special case
// within the vet checker.
//
// See https://golang.org/issues/8005.
type DoNotCopy [0]sync.Mutex
例子如下:
package main
import (
"fmt"
"sync"
"unsafe"
)
type DoNotCopy [0]sync.Mutex
type A1 struct {
_ DoNotCopy // 不影响 A1 结构体大小
F1 int
F2 string
}
type A2 struct {
F1 int
F2 string
}
func TestDoNotCopy(a A1) {
// line 22
fmt.Println(unsafe.Sizeof(A1{
}) == unsafe.Sizeof(A2{
})) // true
}
var a = A1{
}
func main() {
TestDoNotCopy(a) // line 29
}
执行go vet
,会有提示:
./main.go:22:22: TestDoNotCopy passes lock by value: pragma.A1 contains sync.Mutex
./main.go:29:16: call of TestDoNotCopy copies lock value: pragma.A1 contains sync.Mutex