はじめに
Go の勉強のため、A Tour of Goに取り組んでいる
今回はMethods and interfaces
章について、学んだことを記していく
まとめ記事はこちら
使えそうなスニペット
※筆者は VSCode を使用
meth
method を作成する。すごい便利
func (receiver type) method() {
}
tyi
type name interface {
}
ページごとの補足
Methods and pointer indirection
struct と*struct の違いを調べておきたい
The empty interface
使いみち
Typescript におけるunknown
に近い?
Type assertions
使いみち
var i interface{} = "Hello"
s, ok := i.(int) // no panic
var i interface{} = "Hello"
s := i.(int) // panic
Exercise: Stringers
func (ip IPAddr) String() string {
return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3])
}
マジックナンバーを使わない場合、こんな感じになりそう
func (ip IPAddr) String() string {
stringArray := make([]string, 0, len(ip))
for _, i := range ip {
stringArray = append(stringArray, fmt.Sprint(i))
}
return strings.Join(stringArray, ".")
}
Errors
関数の返り値の型にerror
を指定した場合、return した値のError()
メソッドが実行される
Exercise: Errors
naked return ステートメントが便利
error の初期値に nil が勝手に入ってくれる
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("Error: %v", float64(e))
}
func Sqrt(x float64) (z float64, e error) {
if x < 0 {
e = ErrNegativeSqrt(x)
return
}
z = 1
for i := 0; i < 10; i++ {
z -= (z*z - x) / (2 * z)
}
return
}
func main() {
fmt.Println(Sqrt(2)) // 1.414213562373095 <nil>
fmt.Println(Sqrt(-2)) // 0 Error: -2
}
Exercise: Readers
一応バリデーションは通ったが、こんな実装で良いか不明
他の人の回答を見ると、strings.NewReader
を使わずにシンプルに byte スライスにA
を挿入していた
それでいいのか……
type MyReader struct{}
func (m MyReader) Error() string {
return fmt.Sprint("Error!")
}
func (m MyReader) Read(b []byte) (int, error) {
var a string
for i := 0; i < len(b); i++ {
a += "A"
}
r := strings.NewReader(a)
for {
_, err := r.Read(b)
if err == io.EOF {
break
}
}
return len(b), nil
}
Exercise: rot13Reader
実装自体は単純
ただ、変換後の文字列をどう返せばいいのか
悩んだ
Read
の引数に渡ってくる byte に文字を格納することで、その文字列が返る
また、13 を足すか引くかを元々数字で判断していた(b < 65
のように)
しかし、rune
を使うことで、直感的に書くことができる
※ただし、これができるのは一部の文字だけ。日本語は NG
func rot(b byte) byte {
const ROT = 13
switch {
case b < 'A':
return b
case b < 'N':
return b + ROT
case b < 'a':
return b - ROT
case b < 'n':
return b + ROT
case b <= 'z':
return b - ROT
default:
return b
}
}
func (r *rot13Reader) Read(b []byte) (n int, err error) {
n, err = r.r.Read(b)
if err != io.EOF {
for i := range b {
b[i] = rot(b[i])
}
}
return n, err
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
Exercise: Images
struct 初期化時に表示計算用の関数fn
を与えるようにしてみた
package main
import (
"fmt"
"image"
"image/color"
"golang.org/x/tour/pic"
)
type MyImage struct {
x0, y0, x1, y1 int
fn func(x, y int) int
}
func (m MyImage) At(x, y int) color.Color {
v := uint8(m.fn(x, y))
return color.RGBA{v, v, 255, 255}
}
func (m MyImage) Bounds() image.Rectangle {
return image.Rect(m.x0, m.y0, m.x1, m.y1)
}
func (m MyImage) ColorModel() color.Model {
return color.RGBAModel
}
func main() {
fn := func(x, y int) int { return x - y }
m := MyImage{0, 0, 100, 100, fn}
fmt.Println(m.Bounds())
fmt.Println(m.At(0, 0).RGBA())
pic.ShowImage(m)
}