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:
- Field Numbers: Every field must have a unique, non-zero integer tag (e.g.,
= 1). These are permanent; changing them breaks backward compatibility. - Data Types: Use standard protobuf types like
int32,string,bool, orrepeatedfor lists. - Go Package: The
option go_packagetells 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.