Data Types
データ型の取り回しやよくあるユースケース、発展的な使い方について。
文字列
関連項目:
How-to:
- 改行コードを取り除きたい -> strings.TrimRightを使う
参考:
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
では、メモリコピーが走るそうだ
参考:
- golang で string を []byte にキャストするとメモリコピーが走ります - Qiita
- golang で string を []byte にキャストしてもメモリコピーが走らない方法を考えてみる - Qiita
- go - How to assign string to bytes array - Stack Overflow
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:]...)...)
ポインタ
関連項目:
参考:
引数
メモ:
- 基本、よほどデータが大きくならない限りは値渡しでよさそう
- オブジェクトの中身を書き換えるような処理だと、ポインタ渡しじゃないと駄目。そりゃそうか
参考:
- Goでxxxのポインタを取っているプログラムはだいたい全部間違っている - Qiita
- Go言語(golang)における値渡しとポインタ渡しのパフォーマンス影響について - Finatext - Medium
戻り値
参考:
構造体の使い方
関連項目:
参考:
埋め込み
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/stringer
で String()
メソッドを含むコードを自動生成することもできる。
関連項目:
参考:
- GoのEnumイディオム - Qiita
- Big Sky :: Re: GoLangでJavaのenumっぽいライブラリ作った話
- Ten Useful Techniques in Go – Fatih Arslan
- https://godoc.org/golang.org/x/tools/cmd/stringer
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)
}
注意点はメソッドを使うとき:
- レシーバはポインタ型にする。でないと値渡しになって、結果が反映されない
- ので、メソッド内ではデリファレンスして使う
- 呼び出し側で値を初期化してメソッドを呼び出す