How to Define a Protocol Buffer (.proto) File

Web
To define a Protocol Buffer file, create a `.proto` file with a `syntax` declaration, specify the `package`, and define `message` types with field numbers and data types.

To define a Protocol Buffer file, create a .proto file with a syntax declaration, specify the package, and define message types with field numbers and data types. You then compile this file into Go code using the protoc compiler with the Go plugin to generate the necessary structs and serialization methods.

Here is a minimal example of a user.proto file defining a simple user message:

syntax = "proto3";

package user;

option go_package = "example.com/myapp/proto";

message User {
  int64 id = 1;
  string name = 2;
  string email = 3;
  repeated string tags = 4;
}

Key rules to follow:

  1. Field Numbers: Every field must have a unique, non-zero integer tag (e.g., = 1). These are permanent; changing them breaks backward compatibility.
  2. Data Types: Use standard protobuf types like int32, string, bool, or repeated for lists.
  3. Go Package: The option go_package tells the compiler where to place the generated Go files.

To generate the Go code, ensure you have protoc and the Go plugin installed. Run the following command from your project root:

protoc --go_out=. --go_opt=paths=source_relative user.proto

This generates a user.pb.go file containing the User struct and methods like Marshal() and Unmarshal(). You can then import and use it in your Go code:

package main

import (
	"fmt"
	"log"

	"example.com/myapp/proto"
)

func main() {
	// Create a new message
	user := &proto.User{
		Id:    101,
		Name:  "Alice",
		Email: "alice@example.com",
		Tags:  []string{"admin", "active"},
	}

	// Serialize to bytes
	data, err := user.Marshal()
	if err != nil {
		log.Fatal(err)
	}

	// Deserialize back to struct
	var newUser proto.User
	if err := newUser.Unmarshal(data); err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Received: %+v\n", newUser)
}

Remember that proto3 (the current standard) does not require optional keywords for scalar fields; they default to zero values if unset. For complex nested structures, simply define additional message types within the same file or import other .proto files using the import statement.