Protocol Buffer Basics: Go

This tutorial provides a basic Go programmer's introduction to working with protocol buffers, using the proto3 version of the protocol buffers language. By walking through creating a simple example application, it shows you how to

  • Define message formats in a .proto file.
  • Use the protocol buffer compiler.
  • Use the Go protocol buffer API to write and read messages.

This isn't a comprehensive guide to using protocol buffers in Go. For more detailed reference information, see the Protocol Buffer Language Guide, the Go API Reference, the Go Generated Code Guide, and the Encoding Reference.

Why use protocol buffers?



  • Use gobs to serialize Go data structures. This is a good solution in a Go-specific environment, but it doesn't work well if you need to share data with applications written for other platforms.


Where to find the example code

Our example is a set of command-line applications for managing an address book data file, encoded using protocol buffers. The command add_person_go adds a new entry to the data file. The command list_people_go parses the data file and prints the data to the console.


Defining your protocol format



syntax = "proto3";
package tutorial;

import "google/protobuf/timestamp.proto";

In Go, the package name is used as the Go package, unless you have specified a go_package. Even if you do provide a go_package, you should still define a normal package as well to avoid name collisions in the Protocol Buffers name space as well as in non-Go languages.


message Person {
  string name = 1;
  string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;

  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;

  repeated PhoneNumber phones = 4;

  google.protobuf.Timestamp last_updated = 5;

// Our address book file is just one of these.
message AddressBook {
  repeated Person people = 1;






Compiling your protocol buffers


  2. Run the following command to install the Go protocol buffers plugin:
    go get -u
    The compiler plugin protoc-gen-go will be installed in $GOBIN, defaulting to $GOPATH/bin. It must be in your $PATH for the protocol compiler protoc to find it.
    protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/addressbook.proto
    Because you want Go classes, you use the --go_out option – similar options are provided for other supported languages.

This generates addressbook.pb.go in your specified destination directory.

The Protocol Buffer API

Generating addressbook.pb.go gives you the following useful types:

  • An AddressBook structure with a People field.
  • A Person structure with fields for Name, Id, Email and Phones.
  • A Person_PhoneNumber structure, with fields for Number and Type.
  • The type Person_PhoneType and a value defined for each value in the Person.PhoneType enum.

You can read more about the details of exactly what's generated in the Go Generated Code guide, but for the most part you can treat these as perfectly ordinary Go types.

Here's an example from the list_people command's unit tests of how you might create an instance of Person:

p := pb.Person{
	Id:    1234,
	Name:  "John Doe",
	Email: "",
	Phones: []*pb.Person_PhoneNumber{
		{Number: "555-4321", Type: pb.Person_HOME},

Writing a Message

The whole purpose of using protocol buffers is to serialize your data so that it can be parsed elsewhere. In Go, you use the proto library's Marshal function to serialize your protocol buffer data. A pointer to a protocol buffer message's struct implements the proto.Message interface. Calling proto.Marshal returns the protocol buffer, encoded in its wire format. For example, we use this function in the add_person command:

book := &pb.AddressBook{}
// ...

// Write the new address book back to disk.
out, err := proto.Marshal(book)
if err != nil {
	log.Fatalln("Failed to encode address book:", err)
if err := ioutil.WriteFile(fname, out, 0644); err != nil {
	log.Fatalln("Failed to write address book:", err)

Reading a Message

To parse an encoded message, you use the proto library's Unmarshal function. Calling this parses the data in buf as a protocol buffer and places the result in pb. So to parse the file in the list_people command, we use:

// Read the existing address book.
in, err := ioutil.ReadFile(fname)
if err != nil {
	log.Fatalln("Error reading file:", err)
book := &pb.AddressBook{}
if err := proto.Unmarshal(in, book); err != nil {
	log.Fatalln("Failed to parse address book:", err)

Extending a Protocol Buffer


  • you may delete fields.


