Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Go  Module 6 – Interfaces and type assertions

Interfaces you will meet: Stringer to Handler

Implement error, fmt.Stringer and sort.Interface in Go, build a priority queue on container/heap, and write http.Handler types that httptest can call.

  • Intermediate
  • 15 minutes
  • Examples run with Go 1.27.1
  • By MySmartCoPilot
Free core, Pro depth: 6 of 15 minutes free
Free: A few methods connect your type to the library · sort.Interface and heap.Interface: an order you supply · http.Handler: one method between your code and the server. With a pass: 2 in-depth parts and 3 interview answers, about 9 minutes.

What you will learn

  • Implement error and fmt.Stringer, and say which one fmt uses for a value that has both
  • Order a collection through sort.Interface, and build a priority queue on container/heap
  • Write an http.Handler type and an http.HandlerFunc, and call them through httptest
  • Update and remove the items of a container/heap queue with heap.Fix and heap.Remove
  • Use encoding.TextMarshaler so that JSON map keys and flags read and write a type as text

Before you start

What you’ll learn · Before you start

What you will learn

  • Implement error and fmt.Stringer, and say which one fmt uses for a value that has both
  • Order a collection through sort.Interface, and build a priority queue on container/heap
  • Write an http.Handler type and an http.HandlerFunc, and call them through httptest
  • Update and remove the items of a container/heap queue with heap.Fix and heap.Remove
  • Use encoding.TextMarshaler so that JSON map keys and flags read and write a type as text

Before you start

On this page

Three methods, Len, Less and Swap, let sort.Sort order any collection of yours, and two more turn the same type into a priority queue that always hands out the most urgent of eight deliveries first. The standard library is built on small interfaces like these: you write the methods, and a package you did not write does the rest.

You will implement error and fmt.Stringer, order deliveries with sort.Interface and container/heap, and serve requests through http.Handler, the interfaces you will meet in nearly every Go program. Every program here was run with Go 1.27.1, and its output appears beneath it.

A few methods connect your type to the library

Each of these interfaces is a promise: give your type its methods, and the package on the right will call them.

Interface Methods Who calls them
error Error() string everything that reports a failure, and fmt
fmt.Stringer String() string fmt, for %v and Println
sort.Interface Len, Less, Swap sort.Sort and sort.Stable
heap.Interface sort.Interface, Push, Pop container/heap
io.Reader, io.Writer Read, Write io.Copy, bufio, compression, files, networks
http.Handler ServeHTTP net/http’s server, for every request

Here is a seat with a String method, an error type, and a type with both:

print_methods.go: String, Error, and both Go · print_methods.go
// print_methods.go: a String method, an Error method, and a type with both, printed by fmt.
package main

import (
	"errors"
	"fmt"
)

// Seat prints as coach and berth.
type Seat struct {
	Coach string
	Berth int
}

func (s Seat) String() string { return fmt.Sprintf("%s-%d", s.Coach, s.Berth) }

// BerthError is an error: its Error method satisfies the error interface.
type BerthError struct{ Seat Seat }

func (e BerthError) Error() string { return "berth " + e.Seat.String() + " is taken" }

// Fault has both methods.
type Fault struct{}

func (Fault) String() string { return "the String method" }
func (Fault) Error() string  { return "the Error method" }

func main() {
	s := Seat{Coach: "B2", Berth: 34}
	fmt.Println(s, []Seat{s, {Coach: "S1", Berth: 7}})
	var err error = BerthError{Seat: s}
	fmt.Println(err)
	var be BerthError
	fmt.Println(errors.As(err, &be), be.Seat.Berth)
	fmt.Println(Fault{})
}

Output

B2-34 [B2-34 S1-7]
berth B2-34 is taken
true 34
the Error method

Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run print_methods.go

A fmt.Stringer controls how fmt prints a value: the Seat printed as B2-34, inside a slice too. A type with an Error method is an error, the predeclared interface of the specification, so BerthError went into an error variable and errors.As found it again. Fault has both methods, and fmt printed the Error method, because fmt, as its documentation says, checks for error before Stringer. Formatted output with fmt covers String methods in depth.

Checkpoint

Check: error and fmt.Stringer

  1. Question 1 of 2 Seat has func (s Seat) String() string returning "B2-34". What does fmt.Println([]Seat{s}) print?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: [B2-34]

    print_methods.go printed B2-34 [B2-34 S1-7]: the String method reaches into the slice.

  2. Question 2 of 2 Fault has both String and Error methods. What does fmt.Println(Fault{}) print, and why?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: the Error method, because fmt checks for error before Stringer

    For %v and Println, an error's Error method is used first, then a Stringer's String.

sort.Interface and heap.Interface: an order you supply

sort.Interface asks for three methods: Len, Less, which reports whether element i sorts before element j, and Swap. container/heap asks for those three plus Push and Pop, and keeps the smallest element, by your Less, at index 0. One type of eight deliveries does both:

deliveries.go: eight deliveries, sorted and in a heap Go · deliveries.go
// deliveries.go: eight deliveries sorted by sort.Sort, and the same eight in a container/heap queue.
package main

import (
	"container/heap"
	"fmt"
	"sort"
)

// Delivery is an order and the minutes left before it is due.
type Delivery struct {
	Order   int
	DueMins int
}

// ByDue implements sort.Interface and, with Push and Pop, heap.Interface.
type ByDue []Delivery

func (q ByDue) Len() int           { return len(q) }
func (q ByDue) Less(i, j int) bool { return q[i].DueMins < q[j].DueMins }
func (q ByDue) Swap(i, j int)      { q[i], q[j] = q[j], q[i] }

// Push and Pop are for package heap to call; they add and remove at the end.
func (q *ByDue) Push(x any) { *q = append(*q, x.(Delivery)) }
func (q *ByDue) Pop() any {
	old := *q
	last := old[len(old)-1]
	*q = old[:len(old)-1]
	return last
}

func dues(q ByDue) []int {
	out := make([]int, len(q))
	for i, d := range q {
		out[i] = d.DueMins
	}
	return out
}

func main() {
	orders := ByDue{{1041, 35}, {1042, 12}, {1043, 50}, {1044, 8}, {1045, 41}, {1046, 27}, {1047, 19}, {1048, 60}}

	sorted := append(ByDue(nil), orders...)
	sort.Sort(sorted)
	fmt.Println("sorted:      ", dues(sorted))

	q := append(ByDue(nil), orders...)
	heap.Init(&q)
	fmt.Println("heap array:  ", dues(q))
	for range 3 {
		d := heap.Pop(&q).(Delivery)
		fmt.Printf("next: order %d, due in %d min\n", d.Order, d.DueMins)
	}
	fmt.Println("left in heap:", dues(q))
}

Output

sorted:       [8 12 19 27 35 41 50 60]
heap array:   [8 12 19 35 41 27 50 60]
next: order 1044, due in 8 min
next: order 1042, due in 12 min
next: order 1047, due in 19 min
left in heap: [27 35 50 60 41]

Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run deliveries.go

sort.Sort put all eight in order. heap.Init only arranged them as a binary heap: A tree stored in an array, in which every element is at most its children, at positions 2i + 1 and 2i + 2, so the smallest is at index 0. container/heap keeps a slice in this order through its Less method.: in the array [8 12 19 35 41 27 50 60] each element is at most its children at positions 2i + 1 and 2i + 2, which is enough to find the minimum at once. Each heap.Pop removed the most urgent delivery and repaired the heap. Step through the three pops on the same eight due times:

Interactive · step through it Three pops from the heap of eight due times The root leaves; the last key takes its place and sinks while a child is smaller.
Heap as an array: 8 12 19 35 41 27 50 60.812193541275060array:80121192353414275506607
  • being compared or looked at
  • being moved or written
  • in its final place
  • the key, pivot or target

Pseudo-code

  1. extractMin(): min ← a[0]; a[0] ← a[size − 1]; size ← size − 1
  2. i ← 0
  3. while i has a child smaller than a[i]:
  4. c ← the smaller child; swap a[i] and a[c]; i ← c
  5. return min

A min-heap of 8 keys. extractMin removes the root, the smallest key, and repairs the heap from the top.

When it finishes: Taken out: 8 12 19.

Comparisons 0
Swaps 0

The figure took out 8, 12 and 19 and left [27 35 50 60 41], exactly what deliveries.go printed. Push and Pop on the type itself only add and remove at the end of the slice; container/heap’s documentation says they are for the package to call, and you call heap.Push and heap.Pop.

Key idea

sort and container/heap never see your data: they call Len, Less and Swap, so the order is whatever your Less says.

Checkpoint

Check: sort.Interface and heap.Interface

  1. Question 1 of 2 After heap.Init, the heap array of due times was [8 12 19 35 41 27 50 60]. Which due time does the first heap.Pop return?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: 8, the element at index 0

    Each element is at most its children, so the minimum is at the root, and the first pop takes it.

  2. Question 2 of 2 A dispatcher wants the latest due delivery first, for a test of the slowest orders. What is the smallest change to ByDue?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Make Less return q[i].DueMins > q[j].DueMins

    Both sort and container/heap see only Less, so the order is whatever Less says.

http.Handler: one method between your code and the server

An http.Handler is any type with ServeHTTP(w http.ResponseWriter, r *http.Request). The server calls it once per request; httptest lets a program, or a test, make the same call without a network:

handler.go: a Handler with state and a HandlerFunc Go · handler.go
// handler.go: a Handler type with state, a HandlerFunc, and both called through httptest.
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
)

// Stock answers how many units of an item are left. It is an http.Handler: it has ServeHTTP.
type Stock struct{ units map[string]int }

func (s *Stock) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	item := r.URL.Query().Get("item")
	n, ok := s.units[item]
	if !ok {
		http.Error(w, "unknown item "+item, http.StatusNotFound)
		return
	}
	fmt.Fprintf(w, "%s: %d left\n", item, n)
}

func main() {
	stock := &Stock{units: map[string]int{"atta-5kg": 14}}
	health := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "ok") // an ordinary function, adapted into a Handler
	})

	for _, c := range []struct {
		h    http.Handler
		path string
	}{{stock, "/stock?item=atta-5kg"}, {stock, "/stock?item=ghee-1l"}, {health, "/healthz"}} {
		rec := httptest.NewRecorder()
		c.h.ServeHTTP(rec, httptest.NewRequest("GET", c.path, nil))
		fmt.Printf("%-22s %d %q\n", c.path, rec.Code, rec.Body.String())
	}
}

Output

/stock?item=atta-5kg   200 "atta-5kg: 14 left\n"
/stock?item=ghee-1l    404 "unknown item ghee-1l\n"
/healthz               200 "ok\n"

Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run handler.go

*Stock is a Handler because it has ServeHTTP, and it answered 200 for atta and 404 for ghee. The health check was an ordinary function, which http.HandlerFunc(…) converted into a Handler: HandlerFunc is a function type whose ServeHTTP calls the function. Step through the first request:

Interactive · message by message One request to the Stock handler, without a network Step through handler.go's first request. main calls ServeHTTP itself, as the server would, and reads what the recorder kept.
One request to the Stock handler, without a network, step 6 of 6. Messages so far: main to *Stock: ServeHTTP(rec, req); *Stock to recorder: atta-5kg: 14 left; recorder to main: Code 200 and Body; *Stock to recorder: 404 unknown item.maincalls like a test*Stockan http.Handlerrecordera ResponseWriterServeHTTP(rec, req)atta-5kg: 14 leftCode 200 and Body404 unknown item

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. A request, no network. main builds GET /stock?item=atta-5kg with httptest.NewRequest, and a ResponseRecorder that stands in for the connection's response writer.
  2. The whole contract. The server's only call into a handler is ServeHTTP with a ResponseWriter and the request. main makes the same call directly.
  3. The handler reads the request. Stock reads item from the query string and finds 14 units under atta-5kg in its map.
  4. It writes the response. fmt.Fprintf writes the body to the ResponseWriter. Stock never called WriteHeader, so the status is 200.
  5. main reads what was written. The recorder kept the status and the body, so main printed 200 and the text. A test checks the same two fields.
  6. An unknown item. For ghee-1l the lookup fails, and http.Error writes status 404 with the message, which is what the second line printed.

Two more tools build on these interfaces. A container/heap queue can also change or cancel an item in place, with heap.Fix and heap.Remove. And encoding.TextMarshaler lets a type write and read itself as text for JSON keys, flags and logs.

Checkpoint

Check: http.Handler and HandlerFunc

  1. One question Why can http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {...}) be passed where an http.Handler is needed?

    Choose one answer.

    Show the answer: One question

    Answer: HandlerFunc is a function type with a ServeHTTP method that calls the function

    A named function type can have methods, which is how an ordinary function satisfies a one-method interface.

A delivery queue on container/heap, measured Pro: opens with a Pro, Premium or Ultimate pass

A real dispatch queue changes while it runs: deadlines move, orders are cancelled, and the most urgent one must always come out first. This part reproduces the two mistakes that break a heap, keeps one right through updates and cancellations, and counts what each operation costs.

In this part:

  • The queue’s own Pop and Push are not the queue’s operations
  • heap.Fix and heap.Remove need to know where an item is
  • What each operation costs, counted

3 runnable examples (Go) · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

TextMarshaler: one pair of methods for JSON keys, flags and text Pro: opens with a Pro, Premium or Ultimate pass

encoding.TextMarshaler and TextUnmarshaler let a type write itself as text and read itself back, and encoding/json, flag and other packages look for them. This part makes a seat usable as a JSON map key and as a flag, and shows which method json picks when a type has several.

In this part:

  • A struct key works in JSON once it can write itself as text
  • The same methods serve flags and validation
  • Which method json uses when a type has several
  • Text methods become part of the type’s contract

2 runnable examples (Go) · about 4 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Practise

Practice asks about Stringer and error, the ordering interfaces and Handler, beginning with whichever your answers show is weakest:

Practice that adapts to you

Practise this lesson

8 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 8 What does a type need in order to be an error?

    Choose one answer.

    Show the answer: Question 1 of 8

    Answer: A method Error() string

    BerthError has Error, so it went into an error variable and errors.As found it.

  2. Question 2 of 8 What does this print?

    Read the code, then choose one answer.

    type Coach string
    
    func (c Coach) String() string { return "coach " + string(c) }
    
    fmt.Println(Coach("B2"), string(Coach("B2")))
    Show the answer: Question 2 of 8

    Answer: coach B2 B2

    A conversion to the underlying type drops the methods, which is also how a String method avoids calling itself.

  3. Question 3 of 8 Where does container/heap keep the smallest element, by Less?

    Choose one answer.

    Show the answer: Question 3 of 8

    Answer: At index 0, the root

    deliveries.go's heap array began with 8, the smallest due time.

  4. Question 4 of 8 In the heap array [8 12 19 35 41 27 50 60], which values are the children of 12, at index 1?

    Choose one answer.

    Show the answer: Question 4 of 8

    Answer: 35 and 41, at indexes 3 and 4

    Both 35 and 41 are at least 12, so the heap rule holds there.

  5. Question 5 of 8 A teammate writes ByDue's Less as return q[i].DueMins <= q[j].DueMins. Two deliveries are both due in 20 minutes. What do Less(i, j) and Less(j, i) report for them, and what is wrong?

    Choose one answer.

    Show the answer: Question 5 of 8

    Answer: Both report true, so each delivery claims to sort before the other; Less must use <

    Two elements count as equal when Less is false both ways. With <=, two deliveries due in the same minute would each sort before the other, which no strict weak ordering allows.

  6. Question 6 of 8 What makes a type an http.Handler?

    Choose one answer.

    Show the answer: Question 6 of 8

    Answer: A method ServeHTTP(http.ResponseWriter, *http.Request)

    Any type with ServeHTTP satisfies http.Handler, as *Stock did.

  7. Question 7 of 8 A test calls stock.ServeHTTP(rec, httptest.NewRequest("GET", "/stock?item=ghee-1l", nil)). What does rec.Code hold afterwards?

    Choose one answer.

    Show the answer: Question 7 of 8

    Answer: 404

    handler.go printed 404 "unknown item ghee-1l\n" for that request.

  8. Question 8 of 8 Two handlers need the same stock map. Why is a Stock type with ServeHTTP better than two HandlerFuncs that read a package variable?

    Choose one answer.

    Show the answer: Question 8 of 8

    Answer: Each Stock carries its own map, so tests can build one with their own data and run side by side

    Put a handler's dependencies in its value, and every test gets a fresh, independent one.

Interview questions

Warm-up (fresher): Which methods does sort.Interface ask for, and what must Less promise?

Len, the number of elements; Less(i, j), whether element i must sort before element j; and Swap(i, j). Less must describe a strict weak ordering, and the sort documentation gives two of its rules as examples: if Less(i, j) and Less(j, k) are both true, so is Less(i, k), and if both are false, Less(i, k) is false too. Two elements count as equal when Less is false both ways, so write Less with less than: with less than or equal, each of two equal elements would sort before the other, which no strict weak ordering allows.

Three longer questions of the module bank build on this lesson:

  1. Mid to senior

    A dispatcher keeps deliveries in a container/heap queue. Sometimes a delivery due in 5 minutes goes out after ones due in 20. The code calls q.Push(d) when an order arrives and heap.Pop(&q) when a rider becomes available. Find the bug, fix it, and keep the queue right when a deadline changes or an order is cancelled.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    A team writes every handler as an http.HandlerFunc that reads a package-level stock map and a global database handle. Tests change the map and interfere with each other, so they cannot run in parallel. Redesign the handlers and show how a test builds and calls one.

    Model answer, Pro. First step and follow-ups

  3. Mid to senior

    A new endpoint must return fares keyed by seat, where Seat is a struct with Coach and Berth, and json.Marshal of a map[Seat]Fare fails. What does encoding/json need from a map key type, how do you make Seat work as a key and as a query parameter, and what do you test?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • error and fmt.Stringer are one method each; fmt prints with Error before String when a value has both.
  • sort.Interface needs Len, Less and Swap, and heap.Interface adds Push and Pop for container/heap to call; the heap keeps the smallest element by Less at index 0.
  • Any type with ServeHTTP is an http.Handler, HandlerFunc turns a function into one, and httptest calls handlers without a network.

Terms in this lesson

binary heap
A tree stored in an array, in which every element is at most its children, at positions 2i + 1 and 2i + 2, so the smallest is at index 0. container/heap keeps a slice in this order through its Less method.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.