part1の続き
makeとnewの違い
どちらも値を入れずメモリ領域を確保する。違いはポインタが返ってくるかどうか。
// 返ってこない
s := make([]int, 0)
m := make(map[string]int)
ch := make(chan int)
// 返ってくる
var p *int = new(int)
var st = new(struct{})
cf. 中身はnil (メモリ確保していない)
var s []int
var m map[string]int
var ch chan int
var p *int
struct
type Vertex struct {
X, Y int
S string
}
v1とv2は同じ意味。v3とv4は同じ意味。
v1 := Vertex{} // {0 0 }
var v2 Vertex // {0 0 }
v3 := new(Vertex) // &{0 0 }
v4 := &Vertex{} // &{0 0 }
changeVertex := func(v Vertex) {
v.X = 1000
}
changeVertex2 := func(v *Vertex) {
v.X = 1000 // (*v).X = 1000 と同じ意味になる
}
v := Vertex{1, 2, "test"}
changeVertex(v) // 値渡し
fmt.Println(v) // {1 2 test} 値が書き換わっていない
changeVertex2(&v) // 参照渡し
fmt.Println(v) // {1000 2 test} 値が書き換わっている
structオリエンテッド
package main
import "fmt"
type Vertex struct {
x, y int // 先頭が小文字だとpackage内からのみ参照可能
}
// メソッド(値レシーバー)
func (v Vertex) Area() int {
return v.x * v.y
}
// メソッド(ポインタレシーバー) 中身を書き換える!
func (v *Vertex) Scale(i int) {
v.x = v.x * i
v.y = v.y * i
}
// Embedded(継承みたいなやつ)
type Vertex3D struct {
Vertex
z int
}
func (v Vertex3D) Area3D() int {
return v.x * v.y * v.z
}
func (v *Vertex3D) Scale3D(i int) {
v.x *= i
v.y *= i
v.z *= i
}
// コンストラクタ
func New(x, y, z int) *Vertex3D {
return &Vertex3D{Vertex{x, y}, z}
}
func main() {
v := New(4, 5, 6)
v.Scale3D(10)
fmt.Println(v.Area()) // 継承元のメソッドも使える
fmt.Println(v.Area3D())
}
non-struct
自分なりの型とメソッドを作れる。
type MyInt int
func (i MyInt) Double() int {
return int(i * 2)
}
インターフェースとダックタイピング
package main
import "fmt"
// 設計書みたいなイメージ
type Human interface {
Say() string // 実装しないといけない関数を書く
}
type Person struct {
Name string
}
func (p Person) Say() string {
return "Mr." + p.Name
}
// インターフェースのダックタイピング(Say()というメソッドがないと成立しないときに使う)
func DriveCar(human Human) {
if human.Say() == "Mr.Mike" {
fmt.Println("Run")
} else {
fmt.Println("Get out")
}
}
func main() {
var mike Human = Person{"Mike"} // Humanにstructを入れたら、Say()というメソッドがないとだめ
DriveCar(mike)
}
タイプアサーションとswitch type文
// 引数が interface{} →何でも引数として受け入れる!
func do(i interface{}) {
/* タイプアサーション: interfaceを他の型に変える
ii := i.(int)
ss := i.(string) など
*/
// switch type 文
switch v := i.(type) {
case int:
fmt.Println(v * 2)
case string:
fmt.Println(v + "!!!!")
default:
fmt.Printf("I don't know %T.\n", v)
}
}
Stringer
package main
import "fmt"
type Person struct {
Name string
Age int
}
// String()のメソッドを実装すれば fmt の出力を変えることができる!
func (p Person) String() string {
return fmt.Sprintf("My name is %v, I'm %v years old.", p.Name, p.Age)
}
func main() {
mike := Person{"Mike", 20}
fmt.Println(mike) // My name is Mike, I'm 20 years old.
}
カスタムエラー
package main
import "fmt"
// 自分なりのエラーを作る
type UserNotFound struct {
Username string
}
// 作ったエラーにError()のメソッドを実装するとエラー時にこれが出力される
func (e *UserNotFound) Error() string {
return fmt.Sprintf("User not found: %v", e.Username)
}
func myFunc() error {
// something wrong
ok := false
if ok {
return nil
}
return &UserNotFound{Username: "mike"}
}
func main() {
if err := myFunc(); err != nil{
fmt.Println(err) // User not found: mike
}
}
Error()をポインタレシーバーにする理由:エラーが違う場所で起きたときに比較判定するため。
以下サンプルコード。err == io.EOFの部分。
err == errors.New("EOF")としてもうまく行かない。→ r.Read()が io.EOFというエラーを返すため。
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
}
}
}