1, Execise: Errors
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?
package main
import (
"fmt"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("can't Sqrt negative number: %v\n", float64(e))
//return fmt.Sprintf("can't Sqrt negative number: %v\n", e)
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
z := 1.0
//z := x/2
//z := x
for i := 0; i < 10; i++ {
diff := z*z - x
fmt.Printf("z = %v, diff = %v\n", z, diff)
if math.Abs(diff) < 1.0e-15 {
return z, nil
} else {
z -= diff / (2*z)
}
}
fmt.Printf("Max number of trying reached with next z = %v\n", z)
return z, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
Output looks like:
z = 1, diff = -1
z = 1.5, diff = 0.25
z = 1.4166666666666667, diff = 0.006944444444444642
z = 1.4142156862745099, diff = 6.007304882871267e-06
z = 1.4142135623746899, diff = 4.510614104447086e-12
z = 1.4142135623730951, diff = 4.440892098500626e-16
1.4142135623730951 <nil>
0 can't Sqrt negative number: -2
But if you are using in ErrNegativeSqrt.Error():
return fmt.Sprintf("can't Sqrt negative number: %v\n", e)
you will get an 'stack overflow':
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc0200e02f8 stack=[0xc0200e0000, 0xc0400e0000]
fatal error: stack overflow
From the calling stack, we can see that there is an infinite call to ErrNegativeSqrt.Error():
main.ErrNegativeSqrt.Error(...)
/home/dabs/dev/golang/gotour/error/errors.go:30
main.(*ErrNegativeSqrt).Error(0xc006843b40, 0x4b4760, 0xc006843b40)
<autogenerated>:1 +0x99 fp=0xc020160518 sp=0xc0201604c0 pc=0x49df09
fmt.(*pp).handleMethods(0xc006877520, 0x76, 0xc020160801)
/usr/local/go/src/fmt/print.go:624 +0x1db fp=0xc020160788 sp=0xc020160518 pc=0x497f6b
fmt.(*pp).printArg(0xc006877520, 0x4b4760, 0xc006843b40, 0x76)
/usr/local/go/src/fmt/print.go:713 +0x1e4 fp=0xc020160820 sp=0xc020160788 pc=0x498634
fmt.(*pp).doPrintf(0xc006877520, 0x4d2ef7, 0x1f, 0xc020160998, 0x1, 0x1)
/usr/local/go/src/fmt/print.go:1030 +0x15a fp=0xc020160908 sp=0xc020160820 pc=0x49beda
fmt.Sprintf(0x4d2ef7, 0x1f, 0xc020160998, 0x1, 0x1, 0x4b5d00, 0x42d986)
/usr/local/go/src/fmt/print.go:219 +0x66 fp=0xc020160960 sp=0xc020160908 pc=0x4950f6
main.ErrNegativeSqrt.Error(...)
/home/dabs/dev/golang/gotour/error/errors.go:30
main.(*ErrNegativeSqrt).Error(0xc006843b38, 0x4b4760, 0xc006843b38)
<autogenerated>:1 +0x99 fp=0xc0201609b8 sp=0xc020160960 pc=0x49df09
fmt.(*pp).handleMethods(0xc006877450, 0x76, 0xc020160c01)
/usr/local/go/src/fmt/print.go:624 +0x1db fp=0xc020160c28 sp=0xc0201609b8 pc=0x497f6b
fmt.(*pp).printArg(0xc006877450, 0x4b4760, 0xc006843b38, 0x76)
/usr/local/go/src/fmt/print.go:713 +0x1e4 fp=0xc020160cc0 sp=0xc020160c28 pc=0x498634
fmt.(*pp).doPrintf(0xc006877450, 0x4d2ef7, 0x1f, 0xc020160e38, 0x1, 0x1)
/usr/local/go/src/fmt/print.go:1030 +0x15a fp=0xc020160da8 sp=0xc020160cc0 pc=0x49beda
fmt.Sprintf(0x4d2ef7, 0x1f, 0xc020160e38, 0x1, 0x1, 0x4b5d00, 0x42d986)
/usr/local/go/src/fmt/print.go:219 +0x66 fp=0xc020160e00 sp=0xc020160da8 pc=0x4950f6
main.ErrNegativeSqrt.Error(...)
/home/dabs/dev/golang/gotour/error/errors.go:30
main.(*ErrNegativeSqrt).Error(0xc006843b30, 0x4b4760, 0xc006843b30)
<autogenerated>:1 +0x99 fp=0xc020160e58 sp=0xc020160e00 pc=0x49df09
Below is part of handleMethods of print.go:
One solution is: explicitly change e to float64(e).
Another solution is: change printing verbs from %v to %g(for example), so that line 615 is invalid and subsequent v.Error() will never be called.
func (e ErrNegativeSqrt) Error() string {
//return fmt.Sprintf("can't Sqrt negative number: %v\n", float64(e))
return fmt.Sprintf("can't Sqrt negative number: %g\n", e)
}
In fact any verbs as listed below, except %x and %X, can do the trick.
2, Execise: Readers
package main
import (
"golang.org/x/tour/reader"
)
type MyReader struct {}
func (r MyReader) Read(b []byte) (int, error) {
n := cap(b)
b = b[:n]
for i := 0; i < n; i++ {
b[i] = 'A'
}
return n, nil
}
func main() {
reader.Validate(MyReader{})
}
3, Execise: rot13Reader
package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
}
func (rot13 *rot13Reader) Read(b []byte) (int, error) {
size := cap(b)
b = b[:size]
n, err := rot13.r.Read(b)
if err != nil {
return 0, err
}
b = b[:n]
for i := 0; i < n; i++ {
if b[i] >= 'a' && b[i] <= 'z' {
b[i] = 'a' + (b[i] - 'a' + 13) % 26
}
if b[i] >= 'A' && b[i] <= 'Z' {
b[i] = 'A' + (b[i] - 'A' + 13) % 26
}
}
return n, err
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
Output looks like:
(18:36 dabs@CNU1343VF8 readers) > go build rot13reader.go && ./rot13reader
You cracked the code!(18:39 dabs@CNU1343VF8 readers) >
Yeah, that's right, You cracked the code!
4, Execise: Images
package main
import (
"image"
"golang.org/x/tour/pic"
"image/color"
)
type Image struct {
}
func (m Image) Bounds() image.Rectangle {
return image.Rect(0, 0, 100, 100)
}
func (m Image) ColorModel() color.Model {
return color.RGBAModel
}
func (m Image) At(x, y int) color.Color {
v := uint8(x*y)
return color.RGBA{v, v, 255, 255}
}
func main() {
m2 := Image{}
pic.ShowImage(m2)
}