rmoff's random ramblings
about talks

Learning Golang (some rough notes) - S01E06 - Errors

Published Jul 1, 2020 by in Go, Golang, Errors at https://preview.rmoff.net/2020/07/01/learning-golang-some-rough-notes-s01e06-errors/

👉 A Tour of Go : Exercise: Errors

Like Interfaces, the Tour didn’t really do it for me on Errors either. Too absract, and not enough explanation of the code examples for my liking. It also doesn’t cover the errors package which other tutorial do. I’m not clear if that’s because the errors package isn’t used much, or the Tour focusses only on teaching the raw basics.

I’m quickly learning to head to gobyexample.com each time for more reference on things that aren’t making sense (along with https://www.calhoun.io/ too). The errors page on GoByExample is a good one, and I like how it links through to the Go Playground with each example. The Go Blog’s Error handling and Go is also a good reference, and this blog has some recent updates for Go 1.13.

Once I’d gone through the additional links the errors exercise was OK to figure out:

package main

import (
	"fmt"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
	return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}

func Sqrt(x float64) (float64, error) {
	fmt.Printf("\n--\nSqrt called with value: %v\n", x)
	if x < 0 {
		fmt.Printf("\t%v is less than zero. Returning with error.\n", x)
		return 0, ErrNegativeSqrt(x)
	}
	guess := 1.0
	limit := 10
	for i := 0; i < limit; i++ {
		adj := (guess*guess - x) / (2 * guess)
		if result := guess * guess; result == x {
			fmt.Printf("\t✅Guess %d is correct:\t%g\n", i, guess)
			return guess, nil
		} else if result > x {
			fmt.Printf("\t🔺Guess %d is too high:\t%g\n", i, guess)
			guess -= adj
		} else {
			fmt.Printf("\t🔻Guess %d is too low:\t%g\n", i, guess)
			guess -= adj
		}
	}
	return guess, nil

}

func main() {
	for _, x := range []float64{-9, 9} {
		if result, ok := Sqrt(x); ok == nil {
			fmt.Printf("-> result: %v\n", result)
		} else {
			fmt.Printf("** ERROR %v",ok.Error())
		}
	}
}
--
Sqrt called with value: -9
	-9 is less than zero. Returning with error.
** ERROR cannot Sqrt negative number: -9
--
Sqrt called with value: 9
	🔻Guess 0 is too low:	1
	🔺Guess 1 is too high:	5
	🔺Guess 2 is too high:	3.4
	🔺Guess 3 is too high:	3.023529411764706
	🔺Guess 4 is too high:	3.00009155413138
	🔺Guess 5 is too high:	3.000000001396984
	✅Guess 6 is correct:	3
-> result: 3

Try it out: https://play.golang.org/p/mLa5RqwYckb

This bit had me puzzled:

Note: A call to fmt.Sprint(e) inside the Error method will send the program into an infinite loop. You can avoid this by converting e first: fmt.Sprint(float64(e)). Why?

If I changed it to

func (e ErrNegativeSqrt) Error() string {
	return fmt.Sprintf("cannot Sqrt negative number: %v",e)
}

then running it in the Tour window failed (as expected)

--
Sqrt called with value: -9
	-9 is less than zero. Returning with error.

Program exited: status 2.

Running it in VSCode gave another error:

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

and sticking a print debug into the function shows that it’s recursively called:

func (e ErrNegativeSqrt) Error() string {
	fmt.Println("ErrNegativeSqrt.Error")
	return fmt.Sprintf("cannot Sqrt negative number: %v",e)
}
--
Sqrt called with value: -9
	-9 is less than zero. Returning with error.
ErrNegativeSqrt.Error
ErrNegativeSqrt.Error
ErrNegativeSqrt.Error
ErrNegativeSqrt.Error
ErrNegativeSqrt.Error
ErrNegativeSqrt.Error
[…]

But… I don’t understand why. StackOverflow turns up this explanation

fmt.Sprint(e) will call `e.Error() to convert the value e to a string. If the Error() method calls `fmt.Sprint(e),` then the program recurses until out of memory.

You can break the recursion by converting the e to a value without a String or Error method.

Thus e is converted to float64:

func (e ErrNegativeSqrt) Error() string {
	return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}

This comment offers a neat alternative too:

Isn’t it enough to convert e to a type which may have a String/Error method that doesn’t recurse infinitely?

And hence specifying a different verb works:

func (e ErrNegativeSqrt) Error() string {
	return fmt.Sprintf("cannot Sqrt negative number: %f",e)
}
--
Sqrt called with value: -9
	-9 is less than zero. Returning with error.
** ERROR cannot Sqrt negative number: -9.000000

📺 More Episodes… ðŸ”—

  • Kafka and Go

    • S02E00 - Kafka and Go

    • S02E01 - My First Kafka Go Producer

    • S02E02 - Adding error handling to the Producer

    • S02E03 - Kafka Go Consumer (Channel-based)

    • S02E04 - Kafka Go Consumer (Function-based)

    • S02E05 - Kafka Go AdminClient

    • S02E06 - Putting the Producer in a function and handling errors in a Go routine

    • S02E07 - Splitting Go code into separate source files and building a binary executable

    • S02E08 - Checking Kafka advertised.listeners with Go

    • S02E09 - Processing chunked responses before EOF is reached

  • Learning Go

    • S01E00 - Background

    • S01E01 - Pointers

    • S01E02 - Slices

    • S01E03 - Maps

    • S01E04 - Function Closures

    • S01E05 - Interfaces

    • S01E06 - Errors

    • S01E07 - Readers

    • S01E08 - Images

    • S01E09 - Concurrency (Channels, Goroutines)

    • S01E10 - Concurrency (Web Crawler)


Robin Moffatt

Robin Moffatt works on the DevRel team at Confluent. He likes writing about himself in the third person, eating good breakfasts, and drinking good beer.

Story logo

© 2025