Data Types

データ型の取り回しやよくあるユースケース、発展的な使い方について。

文字列

関連項目:

How-to:

参考:

string -> 数値変換

var i int
var s string="123"

// やり方①
i, e := strconv.Atoi(s)
fmt.Println(i) // -> 123

// やり方②
_, e = fmt.Fscan(strings.NewReader(s), &i)

どっちが速いかは比べてない。

strconv.Atoiだと、sの末尾に改行文字が入ってるとエラーになった。
fmt.Fscanlnだったら大丈夫だった。

string <-> []byte変換

// string -> []byte
s := "foobar"
b := []byte(s)

// []byte -> string
string(b)

ただし、 string -> []byte では、メモリコピーが走るそうだ

参考:

ASCII文字 <-> byte変換

s := "ABC"
s[0]       //=> 65
string(82) //=> "R"

繰り返し

bytes, stringsパッケージにRepeat関数がある:

buf := []byte("あいうえお")
str := "あいうえお"

fmt.Println(string(bytes.Repeat(buf, 3)))
fmt.Println(strings.Repeat(str, 3))

参考:

配列・スライス

参考:

スライスのpush/pop/(un)shift

基本操作的な:

// push
slice = append(slice, x)

// pop
x := slice[len(slice)-1]
slice = slice[:len(slice)-1]

// unshift
slice = append([]T{x}, slice...)

// shift
x := slice[0]
slice = slice[1:]

unshiftについて

slice, slice[0] = append(slice[:1], slice[0:]...), 追加要素

↑こういう書き方もあるが、何をやっているか。

これはGoの多値代入を使って、2回の代入を1行で書いている。
具体例とともに分解して示すと、下のようになっている:

slice := []int{1, 2, 3}
x := -1

slice = append(slice[:1], slice[0:]...)
//=> [1, 1, 2, 3]
//slice = append(slice[:1], slice...) でも良い

slice[0] = x
//=> [-1, 1, 2, 3]

もっとスライスを操作

// 単一要素の削除
a = append(a[:i], a[i+1:]...)
// 単一要素の挿入
a = append(a[:i], append([]T{x}, a[i:]...)...)

ポインタ

関連項目:

参考:

引数

メモ:

  • 基本、よほどデータが大きくならない限りは値渡しでよさそう
  • オブジェクトの中身を書き換えるような処理だと、ポインタ渡しじゃないと駄目。そりゃそうか

参考:

戻り値

参考:

構造体の使い方

関連項目:

参考:

埋め込み

struct Aをstruct Bに埋め込むと、Bから直接Aのメンバー変数やメソッドにアクセスできる。
OOPの継承のようなことができる。

※処理をAに委譲しているだけなので、厳密には継承とは異なる。

Examples:

type A struct {
    Name string
    Age  int
}

type B struct {
    A
    // ポインタの場合は *A にして &A{} を渡す
}

func (b B) Print() {
    // 埋め込みで A の Name と Age が使える
    println("name:", b.Name, ", age:", b.Age)
    // 以下でも同じ
    println("name:", b.A.Name, ", age:", b.A.Age)
}

func main() {
    b := B{A{"Tanaka", 31}}
    b.Print() // name: Tanaka, age: 31
}

参考:

無名struct

Examples:

anonymous := struct {
  name string
  age int
}{"Taro YAMADA", 24}

インタフェースの使い方

関連項目:

参考:

ダックタイピング

https://play.golang.org/p/aja9eLk-4-n に動作例を書いた。

Examples:

type walker interface {
	walk()
}

type human struct {
	name string
}

type dog struct {
	name string
}

func (h *human) walk() {
	fmt.Printf("I am %s, walking now.\n", h.name)
}

func (d *dog) walk() {
	fmt.Printf("Bow wow! (%s is walking)", d.name)
}

func watch(w walker) {
	w.walk()
}

func main() {
	h := &human{"Ken"}
	d := &dog{"Hachi"}
	watch(h)
	watch(d)
}

Type switches

型アサーションをswitch文と組合せて、値の型によって処理を分岐できる。
例外処理などでも便利そう。

入門ガイド:

Examples:

switch v := i.(type) {
case int:
  fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
  fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
  fmt.Printf("I don't know about type %T!\n", v)
}

独自型定義

enum

Goにはenumがない。
intの独自型を定義するのがイディオムになっている。

type Fruit int

const (
    Apple Fruit = iota
    Orange
    Banana
)

var myFruit Fruit

この独自型に対して String() メソッドを実装しておくと、名前が引けて便利:

func (f Fruit) String() string {
    switch f {
    case Apple:
        return "Apple"
    case Orange:
        return "Orange"
    case Banana:
        return "Banana"
    default:
        return "Unknown"
    }
}

golang.org/x/tools/cmd/stringerString() メソッドを含むコードを自動生成することもできる。

関連項目:

参考:

mapや配列

Example:

type pos [2]int
type myMap map[string]pos

func (m *myMap) set(k string, p pos) {
	(*m)[k] = p
}

func main() {
	p := [2]int{5, 10}
	m := myMap{}
	m.set("foo", p)
	fmt.Println("m = ", m)
}

注意点はメソッドを使うとき:

  • レシーバはポインタ型にする。でないと値渡しになって、結果が反映されない
  • ので、メソッド内ではデリファレンスして使う
  • 呼び出し側で値を初期化してメソッドを呼び出す