The core proto package provides essential functions for working with protocol buffer messages, including marshaling to/from wire format, message manipulation (cloning, merging, equality), and extension field support. The protoadapt package bridges v1 and v2 proto APIs for backward compatibility.
import (
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/protoadapt"
"google.golang.org/protobuf/reflect/protoreflect"
)Convert protocol buffer messages to and from the wire format binary encoding.
// Marshal returns the wire-format encoding of m
func Marshal(m Message) ([]byte, error)
// Unmarshal parses the wire-format message in b and places the result in m
// The provided message must be mutable (e.g., a non-nil pointer to a message)
func Unmarshal(b []byte, m Message) error
// Size returns the size in bytes of the wire-format encoding of m
func Size(m Message) intUsage example:
msg := &pb.MyMessage{
Name: "example",
Value: 42,
}
// Marshal to wire format
data, err := proto.Marshal(msg)
if err != nil {
log.Fatal(err)
}
// Unmarshal from wire format
msg2 := &pb.MyMessage{}
err = proto.Unmarshal(data, msg2)
if err != nil {
log.Fatal(err)
}
// Get size without marshaling
size := proto.Size(msg)Configure marshaling behavior with fine-grained control over serialization.
// MarshalOptions configures the marshaler
type MarshalOptions struct {
pragma.NoUnkeyedLiterals
// AllowPartial allows messages that have missing required fields to marshal
// without returning an error. If AllowPartial is false (the default),
// Marshal will return an error if there are any missing required fields.
AllowPartial bool
// Deterministic controls whether the same message will always be
// serialized to the same bytes within the same binary.
//
// Setting this option guarantees that repeated serialization of
// the same message will return the same bytes, and that different
// processes of the same binary (which may be executing on different
// machines) will serialize equal messages to the same bytes.
// It has no effect on the resulting size of the encoded message compared
// to a non-deterministic marshal.
//
// Note that the deterministic serialization is NOT canonical across
// languages. It is not guaranteed to remain stable over time. It is
// unstable across different builds with schema changes due to unknown
// fields. Users who need canonical serialization (e.g., persistent
// storage in a canonical form, fingerprinting, etc.) must define
// their own canonicalization specification and implement their own
// serializer rather than relying on this API.
//
// If deterministic serialization is requested, map entries will be
// sorted by keys in lexographical order. This is an implementation
// detail and subject to change.
Deterministic bool
// UseCachedSize indicates that the result of a previous Size call
// may be reused.
//
// Setting this option asserts that:
//
// 1. Size has previously been called on this message with identical
// options (except for UseCachedSize itself).
//
// 2. The message and all its submessages have not changed in any
// way since the Size call. For lazily decoded messages, accessing
// a message results in decoding the message, which is a change.
//
// If either of these invariants is violated,
// the results are undefined and may include panics or corrupted output.
//
// Implementations MAY take this option into account to provide
// better performance, but there is no guarantee that they will do so.
// There is absolutely no guarantee that Size followed by Marshal with
// UseCachedSize set will perform equivalently to Marshal alone.
UseCachedSize bool
}
// Marshal returns the wire-format encoding of m
func (o MarshalOptions) Marshal(m Message) ([]byte, error)
// MarshalAppend appends the wire-format encoding of m to b, returning the result
// This is a less common entry point than Marshal, which is only needed if you
// need to supply your own buffers for performance reasons.
func (o MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error)
// MarshalState returns the wire-format encoding of a message
// This method permits fine-grained control over the marshaler. Most users
// should use Marshal instead.
func (o MarshalOptions) MarshalState(in protoiface.MarshalInput) (protoiface.MarshalOutput, error)
// Size returns the size in bytes of the wire-format encoding of m
func (o MarshalOptions) Size(m Message) intUsage example:
opts := proto.MarshalOptions{
Deterministic: true,
AllowPartial: false,
}
data, err := opts.Marshal(msg)
if err != nil {
log.Fatal(err)
}Configure unmarshaling behavior including merge mode, partial messages, and type resolution.
// UnmarshalOptions configures the unmarshaler
type UnmarshalOptions struct {
pragma.NoUnkeyedLiterals
// Merge merges the input into the destination message.
// The default behavior is to always reset the message before unmarshaling,
// unless Merge is specified.
Merge bool
// AllowPartial accepts input for messages that will result in missing
// required fields. If AllowPartial is false (the default), Unmarshal will
// return an error if there are any missing required fields.
AllowPartial bool
// If DiscardUnknown is set, unknown fields are ignored.
DiscardUnknown bool
// Resolver is used for looking up types when unmarshaling extension fields.
// If nil, this defaults to using protoregistry.GlobalTypes.
Resolver interface {
FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
}
// RecursionLimit limits how deeply messages may be nested.
// If zero, a default limit is applied.
RecursionLimit int
// NoLazyDecoding turns off lazy decoding, which otherwise is enabled by
// default. Lazy decoding only affects submessages (annotated with [lazy =
// true] in the .proto file) within messages that use the Opaque API.
NoLazyDecoding bool
}
// Unmarshal parses the wire-format message in b and places the result in m
// The provided message must be mutable (e.g., a non-nil pointer to a message)
func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error
// UnmarshalState parses a wire-format message and places the result in m
// This method permits fine-grained control over the unmarshaler. Most users
// should use Unmarshal instead.
func (o UnmarshalOptions) UnmarshalState(in protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error)Usage example:
opts := proto.UnmarshalOptions{
DiscardUnknown: true,
AllowPartial: false,
}
msg := &pb.MyMessage{}
err := opts.Unmarshal(data, msg)
if err != nil {
log.Fatal(err)
}Create deep copies of protocol buffer messages.
// Clone returns a deep copy of m
// If the top-level message is invalid, it returns an invalid message as well
func Clone(m Message) Message
// CloneOf returns a deep copy of m
// If the top-level message is invalid, it returns an invalid message as well
func CloneOf[M Message](m M) MUsage example:
original := &pb.MyMessage{Name: "test"}
// Type-erased clone
cloned := proto.Clone(original).(*pb.MyMessage)
// Type-safe generic clone
cloned2 := proto.CloneOf(original)Merge one message into another, combining their field values.
// Merge merges src into dst, which must be a message with the same descriptor
//
// Populated scalar fields in src are copied to dst, while populated singular
// messages in src are merged into dst by recursively calling Merge.
// The elements of every list field in src is appended to the corresponded
// list fields in dst. The entries of every map field in src is copied into
// the corresponding map field in dst, possibly replacing existing entries.
// The unknown fields of src are appended to the unknown fields of dst.
//
// It is semantically equivalent to unmarshaling the encoded form of src into
// dst with the UnmarshalOptions.Merge option specified.
func Merge(dst, src Message)Usage example:
dst := &pb.MyMessage{Name: "original"}
src := &pb.MyMessage{Value: 42}
proto.Merge(dst, src)
// dst now has Name="original" and Value=42Test equality between protocol buffer messages.
// Equal reports whether two messages are equal, by recursively comparing the
// fields of the message.
//
// - Bytes fields are equal if they contain identical bytes. Empty bytes
// (regardless of nil-ness) are considered equal.
//
// - Floating-point fields are equal if they contain the same value. Unlike
// the == operator, a NaN is equal to another NaN.
//
// - Other scalar fields are equal if they contain the same value.
//
// - Message fields are equal if they have the same set of populated known
// and extension field values, and the same set of unknown fields values.
//
// - Lists are equal if they are the same length and each corresponding
// element is equal.
//
// - Maps are equal if they have the same set of keys and the corresponding
// value for each key is equal.
//
// An invalid message is not equal to a valid message. An invalid message
// is only equal to another invalid message of the same type. An invalid
// message often corresponds to a nil pointer of the concrete message type.
// For example, (*pb.M)(nil) is not equal to &pb.M{}. If two valid messages
// marshal to the same bytes under deterministic serialization, then Equal is
// guaranteed to report true.
func Equal(x, y Message) boolUsage example:
msg1 := &pb.MyMessage{Name: "test", Value: 42}
msg2 := &pb.MyMessage{Name: "test", Value: 42}
if proto.Equal(msg1, msg2) {
fmt.Println("Messages are equal")
}Clear all fields in a message.
// Reset clears every field in the message
// The resulting message shares no observable memory with its previous state
// other than the memory for the message itself.
func Reset(m Message)Usage example:
msg := &pb.MyMessage{Name: "test", Value: 42}
proto.Reset(msg)
// msg is now emptyValidate that all required fields are set.
// CheckInitialized returns an error if any required fields in m are not set
func CheckInitialized(m Message) errorUsage example:
msg := &pb.MyMessage{} // Missing required fields
err := proto.CheckInitialized(msg)
if err != nil {
fmt.Println("Message not fully initialized:", err)
}Retrieve the fully-qualified name of a message type.
// MessageName returns the full name of m
// If m is nil, it returns an empty string
func MessageName(m Message) protoreflect.FullNameUsage example:
msg := &pb.MyMessage{}
name := proto.MessageName(msg) // Returns "example.MyMessage"Helper functions to create pointers to scalar values for use with optional fields in proto2.
// Bool stores v in a new bool value and returns a pointer to it
func Bool(v bool) *bool
// Int32 stores v in a new int32 value and returns a pointer to it
func Int32(v int32) *int32
// Int64 stores v in a new int64 value and returns a pointer to it
func Int64(v int64) *int64
// Uint32 stores v in a new uint32 value and returns a pointer to it
func Uint32(v uint32) *uint32
// Uint64 stores v in a new uint64 value and returns a pointer to it
func Uint64(v uint64) *uint64
// Float32 stores v in a new float32 value and returns a pointer to it
func Float32(v float32) *float32
// Float64 stores v in a new float64 value and returns a pointer to it
func Float64(v float64) *float64
// String stores v in a new string value and returns a pointer to it
func String(v string) *stringUsage example:
msg := &pb.MyProto2Message{
OptionalString: proto.String("hello"),
OptionalInt32: proto.Int32(42),
OptionalBool: proto.Bool(true),
}Advanced helpers for working with optional fields and the Opaque API.
// ValueOrDefault returns the protobuf message val if val is not nil, otherwise
// it returns a pointer to an empty val message.
//
// This function allows for translating code from the old Open Struct API to
// the new Opaque API.
func ValueOrDefault[T interface {
*P
Message
}, P any](val T) T
// ValueOrDefaultBytes is like ValueOrDefault but for working with fields of
// type []byte.
func ValueOrDefaultBytes(val []byte) []byte
// ValueOrNil returns nil if has is false, or a pointer to a new variable
// containing the value returned by the specified getter.
//
// This function is similar to the wrappers (proto.Int32(), proto.String(),
// etc.), but is generic (works for any field type) and works with the hasser
// and getter of a field, as opposed to a value.
//
// This is convenient when populating builder fields.
func ValueOrNil[T any](has bool, getter func() T) *TUsage example for ValueOrNil:
hop := attr.GetDirectHop()
injectedRoute := ripb.InjectedRoute_builder{
Prefixes: route.GetPrefixes(),
NextHop: proto.ValueOrNil(hop.HasAddress(), hop.GetAddress),
}Work with extension fields in proto2 messages.
// HasExtension reports whether an extension field is populated
// It returns false if m is invalid or if xt does not extend m
func HasExtension(m Message, xt protoreflect.ExtensionType) bool
// GetExtension retrieves the value for an extension field
// If the field is unpopulated, it returns the default value for scalars and an immutable,
// empty value for lists or messages. It panics if xt does not extend m.
func GetExtension(m Message, xt protoreflect.ExtensionType) any
// SetExtension stores the value of an extension field
// It panics if m is invalid, xt does not extend m, or if type of v is invalid
// for the specified extension field
func SetExtension(m Message, xt protoreflect.ExtensionType, v any)
// ClearExtension clears an extension field such that subsequent HasExtension
// calls return false. It panics if m is invalid or if xt does not extend m
func ClearExtension(m Message, xt protoreflect.ExtensionType)
// RangeExtensions iterates over every populated extension field in m in an
// undefined order, calling f for each extension type and value encountered
// It returns immediately if f returns false. While iterating, mutating operations
// may only be performed on the current extension field
func RangeExtensions(m Message, f func(protoreflect.ExtensionType, any) bool)Extension type mapping for GetExtension and SetExtension:
╔═══════════════════╤═════════════════════════╗
║ Go type │ Protobuf kind ║
╠═══════════════════╪═════════════════════════╣
║ bool │ bool ║
║ int32 │ int32, sint32, sfixed32 ║
║ int64 │ int64, sint64, sfixed64 ║
║ uint32 │ uint32, fixed32 ║
║ uint64 │ uint64, fixed64 ║
║ float32 │ float ║
║ float64 │ double ║
║ string │ string ║
║ []byte │ bytes ║
║ protoreflect.Enum │ enum ║
║ proto.Message │ message, group ║
╚═══════════════════╧═════════════════════════╝Usage example:
// Check if extension is present
if proto.HasExtension(msg, foopb.E_MyExtension) {
// Get extension value with type assertion
ext := proto.GetExtension(msg, foopb.E_MyExtension).(*foopb.MyMessage)
fmt.Println(ext)
}
// Set extension value
proto.SetExtension(msg, foopb.E_MyExtension, &foopb.MyMessage{
Field: "value",
})
// Clear extension
proto.ClearExtension(msg, foopb.E_MyExtension)
// Iterate over all extensions
proto.RangeExtensions(msg, func(xt protoreflect.ExtensionType, v any) bool {
fmt.Printf("Extension %s: %v\n", xt.TypeDescriptor().FullName(), v)
return true // continue iteration
})The core Message type that all protocol buffer messages implement.
// Message is the top-level interface that all messages must implement
// It provides access to a reflective view of a message. Any implementation of
// this interface may be used with all functions in the protobuf module that
// accept a Message, except where otherwise specified.
//
// This is the v2 interface definition for protobuf messages. The v1 interface
// definition is github.com/golang/protobuf/proto.Message.
//
// - To convert a v1 message to a v2 message, use
// google.golang.org/protobuf/protoadapt.MessageV2Of.
// - To convert a v2 message to a v1 message, use
// google.golang.org/protobuf/protoadapt.MessageV1Of.
type Message = protoreflect.ProtoMessageConvert between the original (v1) and new (v2) proto APIs for backward compatibility.
// MessageV1 is the original github.com/golang/protobuf/proto.Message type
type MessageV1 = protoiface.MessageV1
// MessageV1Of converts a v2 message to a v1 message
// It returns nil if m is nil
func MessageV1Of(m MessageV2) MessageV1
// MessageV2 is the google.golang.org/protobuf/proto.Message type used by the
// current google.golang.org/protobuf module, adding support for reflection
type MessageV2 = proto.Message
// MessageV2Of converts a v1 message to a v2 message
// It returns nil if m is nil
func MessageV2Of(m MessageV1) MessageV2Usage example:
import (
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/protoadapt"
oldproto "github.com/golang/protobuf/proto"
)
// Convert v1 message to v2
var v1msg oldproto.Message
v2msg := protoadapt.MessageV2Of(v1msg)
// Convert v2 message to v1
var v2msg proto.Message
v1msg := protoadapt.MessageV1Of(v2msg)// Error matches all errors produced by packages in the protobuf module
// according to errors.Is
var Error errorUsage example:
import (
"errors"
"google.golang.org/protobuf/proto"
)
data := []byte{0xff, 0xff} // Invalid proto data
msg := &pb.MyMessage{}
err := proto.Unmarshal(data, msg)
if errors.Is(err, proto.Error) {
fmt.Println("Protocol buffer error occurred")
}