One of the most ubiquitous interfaces is Stringer defined by the fmt package.
type Stringer interface { String() string }
A Stringer
is a type that can describe itself as a string. The fmt
package (and many others) look for this interface to print values.
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}
Make the IPAddr
type implement fmt.Stringer
to print the address as a dotted quad.
For instance, IPAddr{1, 2, 3, 4}
should print as "1.2.3.4"
.
package main
import "fmt"
type IPAddr [4]byte
// TODO: Add a "String() string" method to IPAddr.
func main() {
hosts := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}
Go programs express error state with error
values.
The error
type is a built-in interface similar to fmt.Stringer
:
type error interface { Error() string }
(As with fmt.Stringer
, the fmt
package looks for the error
interface when printing values.)
Functions often return an error
value, and calling code should handle errors by testing whether the error equals nil
.
i, err := strconv.Atoi("42") if err != nil { fmt.Printf("couldn't convert number: %v\n", err) return } fmt.Println("Converted integer:", i)
A nil error
denotes success; a non-nil error
denotes failure.
package main
import (
"fmt"
"time"
)
type MyError struct {
When time.Time
What string
}
func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}
func run() error {
return &MyError{
time.Now(),
"it didn't work",
}
}
func main() {
if err := run(); err != nil {
fmt.Println(err)
}
}
Copy your Sqrt
function from the earlier exercise and modify it to return an error
value.
Sqrt
should return a non-nil error value when given a negative number, as it doesn't support complex numbers.
Create a new type
type ErrNegativeSqrt float64
and make it an error
by giving it a
func (e ErrNegativeSqrt) Error() string
method such that ErrNegativeSqrt(-2).Error()
returns "cannot Sqrt negative number: -2"
.
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?
Change your Sqrt
function to return an ErrNegativeSqrt
value when given a negative number.
package main
import (
"fmt"
)
func Sqrt(x float64) (float64, error) {
return 0, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
The io
package specifies the io.Reader
interface, which represents the read end of a stream of data.
The Go standard library contains many implementations of this interface, including files, network connections, compressors, ciphers, and others.
The io.Reader
interface has a Read
method:
func (T) Read(b []byte) (n int, err error)
Read
populates the given byte slice with data and returns the number of bytes populated and an error value. It returns an io.EOF
error when the stream ends.
The example code creates a strings.Reader and consumes its output 8 bytes at a time.
package main
import (
"fmt"
"io"
"strings"
)
func main() {
r := strings.NewReader("Hello, Reader!")
b := make([]byte, 8)
for {
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b =%v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
}
Package image defines the Image
interface:
package image type Image interface { ColorModel() color.Model Bounds() Rectangle At(x, y int) color.Color }
Note: the Rectangle
return value of the Bounds
method is actually an image.Rectangle, as the declaration is inside package image
.
(See the documentation for all the details.)
The color.Color
and color.Model
types are also interfaces, but we'll ignore that by using the predefined implementations color.RGBA
and color.RGBAModel
. These interfaces and types are specified by the image/color package
package main
import (
"fmt"
"image"
)
func main() {
m := image.NewRGBA(image.Rect(0, 0, 100, 100))
fmt.Println(m.Bounds())
fmt.Println(m.At(0, 0).RGBA())
}