DEV Community

Cover image for The Golang Trinity: Functions, Methods, Interfaces
Kevin Nambubbi
Kevin Nambubbi

Posted on

The Golang Trinity: Functions, Methods, Interfaces

#go

Function: Does something with inputs
Method Does something attached to a type
Interface Says what methods a type must have

A method is a function with a receiver.
An interface is a set of method signatures.
If a type has those methods → it satisfies the interface. Automatically.

No implements. No inheritance. Just behavior.

// Function
func Add(a, b int) int { return a + b }

type Counter struct{ val int }

// Method (receiver is the "attachment")
func (c Counter) Add(n int) int { return c.val + n }

  1. Function vs. Method // Function func Add(a, b int) int { return a + b }

type Counter struct{ val int }

// Method (receiver is the "attachment")
func (c Counter) Add(n int) int { return c.val + n }
Same logic. Different style.
Use functions for stateless operations.
Use methods when behavior belongs to a type.

  1. The Interface Glue

type Stringer interface {
String() string
}

type User struct{ Name string }

// User now implements Stringer automatically
func (u User) String() string { return u.Name }

func Print(s Stringer) { fmt.Println(s.String()) }

func main() {
Print(User{Name: "Alice"}) // Works!
}
Key: The function Print doesn't care about User. It cares about the String() method.

  1. The Twist: Functions Can Be Interfaces This is the pattern that confuses people.

// An interface
type Handler interface {
Handle(string)
}

// A function type
type HandlerFunc func(string)

// The trick: attach the Handle method to the function type
func (f HandlerFunc) Handle(s string) {
f(s) // calls itself
}

// Now any function with signature func(string) works as a Handler
func Log(s string) { fmt.Println("LOG:", s) }

func main() {
var h Handler = HandlerFunc(Log)
h.Handle("test") // LOG: test
}
Why is this useful?
It lets you use simple functions where interfaces are expected.
The standard http.HandlerFunc works exactly like this.

  1. Putting It All Together go type Greeter interface { Greet() string }

type Person struct{ Name string }
func (p Person) Greet() string { return "Hi, " + p.Name }

type GreetFunc func() string
func (gf GreetFunc) Greet() string { return gf() }

func Party(g Greeter) { fmt.Println(g.Greet()) }

func main() {
Party(Person{Name: "Bob"}) // Method
Party(GreetFunc(func() string { // Function as interface
return "Surprise!"
}))
}
The Golden Rule
Accept interfaces, return structs.

Functions orchestrate.

Methods define behavior on types.

Interfaces abstract the what from the how.

Your Turn
What's your favorite small interface from the standard library?
io.Reader? fmt.Stringer? http.Handler?

Drop it below 👇

Top comments (0)