Backend Development 12 min read

Master Go Method Sets, Interfaces, and Embedding: A Deep Dive

This article explains Go's method sets, the relationship between receivers and method sets, how interfaces work, and the nuances of embedding both value and pointer types, providing clear code examples and practical insights for Go developers.

360 Zhihui Cloud Developer
360 Zhihui Cloud Developer
360 Zhihui Cloud Developer
Master Go Method Sets, Interfaces, and Embedding: A Deep Dive

Go's built‑in high‑concurrency features make it popular in cloud computing, but many developers still misunderstand its method sets. This guide reviews method sets, their interaction with receivers, interfaces, and embedding, supported by concrete code examples.

1. What Is a Method Set

In Go, each type has an associated method set containing all methods declared with that type as the receiver.

<code>type Student struct {
    age int8
    name string
}

func (s Student) showName() {
    fmt.Println(s.name)
}

func (s *Student) setName(newName string) {
    s.name = newName
}
</code>

The Student type’s method set includes showName , while *Student includes both showName and setName because a pointer method set contains all value‑receiver and pointer‑receiver methods.

2. Relationship Between Method Sets and Receivers

Even though Student does not list setName in its method set, a value of type Student can still call setName because the compiler automatically takes the address.

<code>s := Student{}
s.setName("dq") // compiled as (&s).setName("dq")
</code>

Key points:

Receiver names are optional; the type can be omitted.

Receivers may be T or *T , but T cannot be an interface or pointer type.

Go does not support method overloading.

Both value and pointer variables can invoke all methods; the compiler performs the necessary conversion.

3. Method Sets and Interfaces

An interface is a set of method signatures. A type implements an interface if its method set contains all the interface’s methods, regardless of explicit declaration.

<code>type Personer interface { // interface definition
    showName()
}

type Student struct {
    Personer // embedded interface
}
</code>

Interface values consist of an interface table (metadata) and a data pointer to the concrete value.

<code>package main

import (
    "fmt"
    "reflect"
)

type User struct { id int; name string }

type Student struct { id int; name string }

func main() {
    u := User{1, "Tom"}
    var i interface{} = u // any type implements empty interface
    fmt.Println(reflect.TypeOf(i)) // main.User
    i = Student{}
    fmt.Println(reflect.TypeOf(i)) // main.Student
}
</code>

When a concrete type implements an interface with a value receiver, both the value and its pointer satisfy the interface; with a pointer receiver, only the pointer satisfies it.

<code>type Animal interface { say() }

type Dog struct { name string }

type Cat struct { name string }

func (d Dog) say() { fmt.Println("I am", d.name) }
func (c *Cat) say() { fmt.Println("I am", c.name) }

func main() {
    var a Animal = Dog{name:"A"}   // OK, value implements
    a.say()
    var b Animal = &Dog{name:"B"} // OK, pointer implements
    b.say()
    var c Animal = Cat{name:"C"}   // compile error: Cat value does not implement
    var d Animal = &Cat{name:"D"} // OK, pointer implements
    d.say()
}
</code>

4. Method Sets and Embedding

Embedding places one type as an anonymous field inside another, enabling a form of inheritance.

<code>type Person struct { age int8; name string }

type Student struct { Person // embedded Person
}

var s = Student{}
s.name = "dq"
</code>

Embedding a value type vs. a pointer type changes default values and method‑set composition.

<code>type Student1 struct { Person }   // value embedding

type Student2 struct { *Person } // pointer embedding
</code>

Value‑embedded fields have their zero value (e.g., Person{age:0, name:""} ), while pointer‑embedded fields default to nil and must be initialized before use.

<code>type Person struct { age int8; name string }

func (p Person) showName() { fmt.Println(p.name) }
func (p *Person) setName(n string) { p.name = n }

type Student1 struct { Person }
type Student2 struct { *Person }

s1 := Student1{}
s1.setName("student1_01") // ok
s1.showName()

s2 := &Student2{}
s2.setName("student1_02") // runtime panic: nil pointer dereference

s3 := &Student2{Person: &Person{age:3, name:"s3"}}
s3.showName()
</code>

5. Embedding and Interface Implementation

When a struct embeds another type, the embedding struct’s method set includes the embedded type’s methods according to the embedding rules.

<code>type Human interface { showName(); setName(string) }

type Person struct { age int8; name string }
func (p Person) showName() { fmt.Println(p.name) }
func (p *Person) setName(n string) { p.name = n }

type Student1 struct { Person }   // value embedding
type Student2 struct { *Person }  // pointer embedding
</code>

Analysis:

Student1’s value type only has showName , so it does not implement Human . Its pointer type includes both methods and does implement the interface.

Student2’s both value and pointer types implement Human because the embedded field is a pointer.

<code>var s1 Human = Student1{}          // compile error: missing setName
var s2 Human = &Student1{}         // ok
var s3 Human = Student2{&Person{}}   // ok
var s4 Human = &Student2{&Person{}} // ok
</code>
Go method set illustration
Go method set illustration
backendProgrammingGoEmbeddinginterface{}method set
360 Zhihui Cloud Developer
Written by

360 Zhihui Cloud Developer

360 Zhihui Cloud is an enterprise open service platform that aims to "aggregate data value and empower an intelligent future," leveraging 360's extensive product and technology resources to deliver platform services to customers.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.