GO

Go Fundamentals

18 lessons

Progress0%
1. Introduction to Go
1What is Go?
2. Variables and Data Types
1Data Types in Go
3. Control Flow
If, For, and SwitchDefer, Panic, Recover
4. Functions
Function BasicsError Handling
5. Structs and Methods
StructsMethods and Interfaces
6. Concurrency
Goroutines and ChannelsSelect and Sync
7. Maps & Slices Advanced
Slices Deep DiveMaps Operations & Patterns
8. Interfaces Deep Dive
Interface Composition & anyCommon Interfaces & Patterns
9. Packages & Modules
Package SystemGo Modules & Workspace
10. Testing & Standard Library
Testing in GoStandard Library Essentials
All Tutorials
GoStructs and Methods
Lesson 8 of 18 min
Chapter 5 · Lesson 2

Methods and Interfaces

Methods and Interfaces in Go

Method receivers Methods are defined with a receiver — a parameter between func and the method name:

go
func (r Rectangle) Area() float64 { … }  // value receiver
func (c *Counter) Increment() { c.n++ }  // pointer receiver
  • Value receiver: receives a copy. Use when the method doesn't modify state.
  • Pointer receiver: receives a pointer to the original. Use when modifying state or for large structs.

Interfaces An interface specifies a set of method signatures. A type implements an interface implicitly — no implements keyword needed:

go
type Stringer interface { String() string }

Any type with a String() string method satisfies Stringer.

Empty interface interface{} (or any) The empty interface is satisfied by every type — similar to Object in other languages.

Type assertions Extract the concrete value from an interface:

go
s, ok := val.(string)

Key points:

  • Interfaces enable polymorphism and dependency inversion in Go.
  • Prefer small, focused interfaces (often one or two methods).
  • The standard library's io.Reader, io.Writer, and fmt.Stringer are canonical examples.

Code Examples

Value vs pointer receiversgo
package main

import "fmt"

type Counter struct {
	count int
}

func (c Counter) Value() int {
	return c.count
}

func (c *Counter) Increment() {
	c.count++
}

func (c *Counter) Reset() {
	c.count = 0
}

func main() {
	c := Counter{}
	c.Increment()
	c.Increment()
	c.Increment()
	fmt.Println("Count:", c.Value())
	c.Reset()
	fmt.Println("After reset:", c.Value())
}

Pointer receivers modify the original struct. Value receivers operate on a copy and are fine for read-only methods.

Interface satisfaction and type assertiongo
package main

import (
	"fmt"
	"math"
)

type Shape interface {
	Area() float64
	Perimeter() float64
}

type Circle struct{ Radius float64 }
func (c Circle) Area() float64      { return math.Pi * c.Radius * c.Radius }
func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius }

type Rect struct{ W, H float64 }
func (r Rect) Area() float64      { return r.W * r.H }
func (r Rect) Perimeter() float64 { return 2 * (r.W + r.H) }

func printShape(s Shape) {
	fmt.Printf("%T — Area: %.2f, Perimeter: %.2f\n", s, s.Area(), s.Perimeter())
}

func main() {
	shapes := []Shape{Circle{5}, Rect{4, 6}}
	for _, s := range shapes {
		printShape(s)
		if c, ok := s.(Circle); ok {
			fmt.Printf("  Radius: %.1f\n", c.Radius)
		}
	}
}

Both types implicitly satisfy Shape. Type assertions extract the concrete type; the ok form is safe and avoids panics.

Quick Quiz

1. How does a type implement an interface in Go?

2. When should you use a pointer receiver instead of a value receiver?

Was this lesson helpful?

PreviousNext