Go FAQ
バージョン
github.com/golang/protobuf
と google.golang.org/protobuf
の違いは何ですか?
github.com/golang/protobuf
モジュールは、オリジナルの Go プロトコルバッファ API です。
google.golang.org/protobuf
モジュールは、シンプルさ、使いやすさ、安全性のために設計された、この API の更新バージョンです。更新された API の主要な機能は、リフレクションのサポートと、ユーザー向け API と基盤となる実装の分離です。
新しいコードでは google.golang.org/protobuf
を使用することをお勧めします。
github.com/golang/protobuf
のバージョン v1.4.0
以降は、新しい実装をラップし、プログラムが新しい API を段階的に採用できるようにします。たとえば、github.com/golang/protobuf/ptypes
で定義されている Well-Known Types は、新しいモジュールで定義されているものの単なるエイリアスです。したがって、google.golang.org/protobuf/types/known/emptypb
と github.com/golang/protobuf/ptypes/empty
は相互に交換可能です。
proto1
、proto2
、proto3
とは何ですか?
これらはプロトコルバッファの*言語*のリビジョンです。これはprotobufのGo*実装*とは異なります。
proto3
は現在の言語バージョンです。これは最も一般的に使用されている言語バージョンです。新しいコードでは proto3 を使用することをお勧めします。proto2
は古い言語バージョンです。proto3 に置き換えられましたが、proto2 は引き続き完全にサポートされています。proto1
は廃止された言語バージョンです。オープンソースとしてリリースされることはありませんでした。
いくつかの異なる Message
型があります。どれを使うべきですか?
"google.golang.org/protobuf/proto".Message
は、現在のバージョンのプロトコルバッファコンパイラによって生成されたすべてのメッセージによって実装されるインターフェース型です。proto.Marshal
やproto.Clone
のように、任意のメッセージを操作する関数は、この型を受け入れたり返したりします。"google.golang.org/protobuf/reflect/protoreflect".Message
は、メッセージのリフレクションビューを記述するインターフェース型です。proto.Message
でProtoReflect
メソッドを呼び出して、protoreflect.Message
を取得します。"google.golang.org/protobuf/reflect/protoreflect".ProtoMessage
は"google.golang.org/protobuf/proto".Message
のエイリアスです。これら2つの型は相互に交換可能です。"github.com/golang/protobuf/proto".Message
は、レガシーな Go プロトコルバッファ API によって定義されたインターフェース型です。すべての生成されたメッセージ型はこのインターフェースを実装しますが、このインターフェースはこれらのメッセージに期待される動作を記述していません。新しいコードはこの型を使用することを避けるべきです。
よくある問題
「go install
」: 作業ディレクトリはモジュールの一部ではありません
Go 1.15 以前では、環境変数 GO111MODULE=on
を設定しており、モジュールディレクトリ外で go install
コマンドを実行しています。GO111MODULE=auto
に設定するか、環境変数を設定解除してください。
Go 1.16 以降では、go install
は明示的なバージョンを指定することでモジュールの外部から呼び出すことができます: go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
定数 -1 が protoimpl.EnforceVersion をオーバーフローします
より新しいバージョンの "google.golang.org/protobuf"
モジュールを必要とする生成された .pb.go
ファイルを使用しています。
次のようにして新しいバージョンに更新してください。
go get -u google.golang.org/protobuf/proto
未定義: "github.com/golang/protobuf/proto".ProtoPackageIsVersion4
より新しいバージョンの "github.com/golang/protobuf"
モジュールを必要とする生成された .pb.go
ファイルを使用しています。
次のようにして新しいバージョンに更新してください。
go get -u github.com/golang/protobuf/proto
プロトコルバッファの名前空間の競合とは何ですか?
Go バイナリにリンクされたすべてのプロトコルバッファ宣言は、グローバルレジストリに挿入されます。
すべての protobuf 宣言(例:enum、enum 値、メッセージ)には絶対名があり、これは パッケージ名 と .proto
ソースファイル内の宣言の相対名(例:my.proto.package.MyMessage.NestedMessage
)を連結したものです。protobuf 言語は、すべての宣言が普遍的に一意であると仮定しています。
Go バイナリにリンクされた2つの protobuf 宣言が同じ名前を持つ場合、名前空間の競合が発生し、レジストリがその宣言を名前で適切に解決することが不可能になります。使用されている Go protobuf のバージョンに応じて、これは初期化時にパニックを引き起こすか、競合を黙って無視し、後で実行時に潜在的なバグにつながる可能性があります。
プロトコルバッファの名前空間の競合を修正するにはどうすればよいですか?
名前空間の競合を修正する最善の方法は、競合が発生している理由によって異なります。
名前空間の競合がよく発生する一般的な方法
ベンダー化された .proto ファイル。 1つの
.proto
ファイルが2つ以上の Go パッケージに生成され、同じ Go バイナリにリンクされると、生成された Go パッケージ内のすべての protobuf 宣言で競合します。これは通常、.proto
ファイルがベンダー化され、そこから Go パッケージが生成された場合、または生成された Go パッケージ自体がベンダー化された場合に発生します。ユーザーはベンダー化を避け、代わりにその.proto
ファイルのための一元化された Go パッケージに依存すべきです。.proto
ファイルが外部の所有であり、go_package
オプションがない場合は、その.proto
ファイルの所有者と協力して、多数のユーザーが依存できる一元化された Go パッケージを指定する必要があります。
プロトパッケージ名の欠落または汎用性。
.proto
ファイルがパッケージ名を指定しないか、汎用的なパッケージ名(例:「my_service」)を使用する場合、そのファイル内の宣言が他の場所の宣言と競合する可能性が非常に高くなります。すべての.proto
ファイルには、普遍的に一意になるように意図的に選択されたパッケージ名(例:会社名のプレフィックスを付ける)を含めることをお勧めします。
警告
.proto
ファイルのパッケージ名を遡及的に変更することは、拡張フィールドとして使用される型、google.protobuf.Any
に格納される型、または gRPC サービス定義に対して後方互換性がありません。google.golang.org/protobuf
モジュールの v1.26.0 以降では、複数の競合する protobuf 名がリンクされている Go プログラムが起動すると、ハードエラーが報告されます。競合の原因を修正することが望ましいですが、この致命的なエラーは、次の2つの方法のいずれかで直ちに回避できます。
コンパイル時。 競合を処理するためのデフォルトの動作は、リンカー初期化変数でコンパイル時に指定できます:
go build -ldflags "-X google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn"
プログラム実行時。 特定の Go バイナリを実行する際の競合処理動作は、環境変数で設定できます:
GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn ./main
なぜ reflect.DeepEqual
はprotobufメッセージに対して予期せぬ挙動をするのですか?
生成されたプロトコルバッファメッセージ型には、同等のメッセージ間でも異なる可能性のある内部状態が含まれています。
さらに、reflect.DeepEqual
関数はプロトコルバッファメッセージのセマンティクスを認識しておらず、違いがない場合でも違いを報告することがあります。たとえば、nil
マップを含むマップフィールドと、長さゼロの非nil
マップを含むマップフィールドはセマンティクス的には同等ですが、reflect.DeepEqual
によっては等しくないと報告されます。
メッセージ値を比較するには、proto.Equal
関数を使用してください。
テストでは、"github.com/google/go-cmp/cmp"
パッケージと protocmp.Transform()
オプションを使用することもできます。cmp
パッケージは任意のデータ構造を比較でき、cmp.Diff
は値間の違いを人間が読める形式で報告します。
if diff := cmp.Diff(a, b, protocmp.Transform()); diff != "" {
t.Errorf("unexpected difference:\n%v", diff)
}
ハイラムの法則
ハイラムの法則とは何ですか、そしてなぜこのFAQに含まれているのですか?
ハイラムの法則は次のように述べています。
API の十分な数のユーザーがいれば、契約で何を約束しようとも関係ない。システムのすべての観測可能な動作は、誰かに依存されるだろう。
Go プロトコルバッファ API の最新バージョンの設計目標は、将来にわたって安定性を保証できない観測可能な挙動を可能な限り提供しないことです。私たちは、約束していない領域での意図的な不安定性は、誤った仮定にプロジェクトが長く依存した後になって将来変更されるような、安定性の幻想を与えるよりも優れているという哲学を持っています。
なぜエラーのテキストは変わり続けるのですか?
エラーの正確なテキストに依存するテストは脆く、そのテキストが変更されると頻繁に壊れます。テストでのエラーテキストの安全でない使用を抑制するため、このモジュールによって生成されるエラーのテキストは意図的に不安定です。
エラーがprotobuf
モジュールによって生成されたかどうかを識別する必要がある場合、すべてerrors.Is
に準拠してproto.Error
に一致することを保証します。
なぜ protojson
の出力は変わり続けるのですか?
Go の プロトコルバッファ用 JSON 形式の実装の長期的な安定性については、何の保証もしていません。仕様は有効な JSON が何であるかのみを規定しており、マーシャラーが特定のメッセージを*正確に*フォーマットする方法についての*正規の*形式の仕様は提供していません。出力が安定しているという幻想を与えることを避けるため、バイトごとの比較が失敗する可能性のある小さな違いを意図的に導入しています。
ある程度の出力の安定性を得るためには、出力をJSONフォーマッタに通すことをお勧めします。
なぜ prototext
の出力は変わり続けるのですか?
Go のテキスト形式実装の長期的な安定性については保証していません。protobuf テキスト形式の正規の仕様はなく、将来 prototext
パッケージの出力に改善を加える能力を保持したいと考えています。パッケージの出力の安定性を約束しないため、ユーザーがそれに依存することを抑制するために意図的に不安定さを導入しています。
ある程度の安定性を得るには、prototext
の出力を txtpbfmt
プログラムに通すことをお勧めします。このフォーマッタは、Go で parser.Format
を使用して直接呼び出すことができます。
その他
プロトコルバッファメッセージをハッシュキーとして使用するにはどうすればよいですか?
マーシャリングされたプロトコルバッファメッセージの出力が時間とともに安定することが保証される正規シリアライゼーションが必要です。残念ながら、現時点では正規シリアライゼーションの仕様は存在しません。ご自身で作成するか、必要としない方法を見つける必要があります。
Go プロトコルバッファ実装に新しい機能を追加できますか?
たぶん。ご提案はいつでも歓迎しますが、新しいものを追加することには非常に慎重です。
Go のプロトコルバッファ実装は、他の言語実装と一貫性があるように努めています。そのため、Go に過度に特化した機能は避ける傾向があります。Go 固有の機能は、プロトコルバッファが言語に依存しないデータ交換形式であるという目標を妨げます。
あなたのアイデアが Go 実装に特有のものでない限り、protobuf ディスカッショングループに参加してそこで提案してください。
Go 実装に関するアイデアがある場合は、イシュートラッカーに課題を提出してください: https://github.com/golang/protobuf/issues
Marshal
または Unmarshal
にオプションを追加してカスタマイズできますか?
そのオプションが他の実装(例:C++、Java)に存在する場合のみです。プロトコルバッファのエンコーディング(バイナリ、JSON、テキスト)は実装間で一貫していなければならないため、ある言語で書かれたプログラムは、別の言語で書かれたメッセージを読み取ることができます。
Marshal
関数によって出力されるデータや、Unmarshal
関数によって読み取られるデータに影響を与えるオプションを Go 実装に追加することはありません。ただし、同等のオプションが少なくとも1つの他のサポートされている実装に存在する場合はこの限りではありません。
protoc-gen-go
によって生成されるコードをカスタマイズできますか?
一般的には、いいえ。プロトコルバッファは言語に依存しないデータ交換形式を意図しており、実装固有のカスタマイズはその意図に反します。