メインコンテンツへスキップする

Optional Fields

Protobufに共通する問題は、nilの表現方法です。ゼロ値のプリミティブ・フィールドはバイナリ表現にエンコードされていないため、アプリケーションはプリミティブ・フィールドのゼロ値とnot-setを区別することができません。

これをサポートするために、Protobufプロジェクトでは「ラッパー型」と呼ばれるいくつかのWell-Known typesをサポートしています。 例えば、boolのラッパー型は、google.protobuf.BoolValueと呼ばれ、次のように定義されています:

// Wrapper message for `bool`.
//
// The JSON representation for `BoolValue` is JSON `true` and `false`.
message BoolValue {
// The bool value.
bool value = 1;
}

entprotoがProtobufメッセージ定義を生成するとき、これらのラッパー型を使用してOptionalなentフィールドを表現します。

実際に、Optionalなフィールドを含むようにentスキーマを変更して見ましょう:

// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").
Unique().
Annotations(
entproto.Field(2),
),
field.String("email_address").
Unique().
Annotations(
entproto.Field(3),
),
field.String("alias").
Optional().
Annotations(entproto.Field(4)),
}
}

go generate ./...を再実行して、UserのProtobuf定義が次のようになったことを確認します:

message User {
int32 id = 1;

string name = 2;

string email_address = 3;

google.protobuf.StringValue alias = 4; // <-- これが追加された
}

生成されたサービスの実装もこのフィールドを利用します。 entpb_user_service.go を見てみましょう:

// Create implements UserServiceServer.Create
func (svc *UserService) Create(ctx context.Context, req *CreateUserRequest) (*User, error) {
user := req.GetUser()
m := svc.client.User.Create()
if user.GetAlias() != nil {
m.SetAlias(user.GetAlias().GetValue())
}
m.SetEmailAddress(user.GetEmailAddress())
m.SetName(user.GetName())
res, err := m.Save(ctx)

switch {
case err == nil:
return toProtoUser(res), nil
case sqlgraph.IsUniqueConstraintError(err):
return nil, status.Errorf(codes.AlreadyExists, "already exists: %s", err)
case ent.IsConstraintError(err):
return nil, status.Errorf(codes.InvalidArgument, "invalid argument: %s", err)
default:
return nil, status.Errorf(codes.Internal, "internal: %s", err)
}
}

クライアントコードでラッパー型を利用するには、 wrapperspbパッケージが提供するヘルパー・メソッドを利用して、 ラッパー型のインスタンスを簡単に構築することができます。 例 cmd/client/main.go

func randomUser() *entpb.User {
return &entpb.User{
Name: fmt.Sprintf("user_%d", rand.Int()),
EmailAddress: fmt.Sprintf("user_%d@example.com", rand.Int()),
Alias: wrapperspb.String("John Doe"),
}
}