Types with methods

July 21, 2023

Type definitions

A defined type may have methods associated with it. It does not inherit any methods bound to the given type, but the method set of an interface type or of elements of a composite type remains unchanged:

// A Mutex is a data type with two methods, Lock and Unlock.
type Mutex struct         { /* Mutex fields */ }
func (m *Mutex) Lock()    { /* Lock implementation */ }
func (m *Mutex) Unlock()  { /* Unlock implementation */ }

// NewMutex has the same composition as Mutex but its method set is empty.
type NewMutex Mutex

// The method set of PtrMutex's underlying type *Mutex remains unchanged,
// but the method set of PtrMutex is empty.
type PtrMutex *Mutex

// The method set of *PrintableMutex contains the methods
// Lock and Unlock bound to its embedded field Mutex.
type PrintableMutex struct {
	Mutex
}

// MyBlock is an interface type that has the same method set as Block.
type MyBlock Block

The above examples are pretty good at showing the behavior, but let’s talk a bit about intention.

Why would you ever want to define a type with the same composition as another, but with an empty method set?

One example I’ve written about before, is to break an otherwise infinite loop when implementing a custom JSON marshaler. Say you want to marshal a value as JSON array:

func (t Tag) MarshalJSON() ([]byte, error) {
    type localTag Tag // localTag doesn't inherit the MarshalJSON method, breaking the loop
    jsonValue, err := json.Marshal(localTag(t))
    if err != nil {
        return nil, err
    }
    return json.Marshal([]json.RawMessage{jsonValue})
}

Quotes from The Go Programming Language Specification Version of December 15, 2022


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .