Closed17
Go言語プログラミングエッセンス
switch
- caseに式かける
- fallthroughで次のcaseに降下する
スライスの要素を削除する方法
append
n := 50
a = append(a[:n], a[n+1:]...)
部分参照+copy
アロケーションが発生しないので上より良い
n := 50
a = a[:n+copy(a[n:], a[n+1:])]
Unicode文字列を扱う
string
ではなく[]rune
を使うと再代入できたりする
func TestString(t *testing.T) {
s := "あいうえお"
rs := []rune(s)
if len(rs) != utf8.RuneCountInString(s) {
t.Error(len(rs))
}
rs[0] = 'か'
if string(rs) != "かいうえお" {
t.Error(s)
}
}
デリファレンス
参照先を見に行くこと
*v
エスケープ解析
- ポインタが関数の外に返されるか自動で判断し、スタックからヒープに切り替える
- C言語にはエスケープ解析がないため、関数内の変数のポインタを返すとスタックを参照してクラッシュする
defer
- defer実行に使う変数は、defer指定時にキャプチャされる
- deferに無名関数を指定する場合、無名関数内で使う変数はキャプチャされない
- 指定順と逆に実行される
select
- 複数のチャネルからデータを同時に待つのに使える
- defaultを記述するとブロッキングされない
- データの入力がない場合に、別の処理を行うのに使える
goモジュール
-
/examples/
でのみ依存しているパッケージがある場合、/examples/go.mod
を作ることで/go.mod
に依存を記述しなくて済む - replaceでローカルディレクトリのパッケージを参照できる
interfaceを実装しているかチェックする
インターフェースの型を持つ変数に、実装型のポインタ型のnilを代入しておくとIDEが検知してくれる
package main
var _ A = (*B)(nil)
type A interface {
Do()
}
type B struct {
}
func (b *B) Do() {
}
ランタイムでpanicが発生するケース
- ゼロ除算
- nilポインタのデリファレンス
- 配列の境界外アクセス
recover
- defer内で実行してpanicから復帰する
- panicが発生したかは、
recover()
の返り値で判断する - ランタイムpanicの場合、
recover()
の返り値にはerror型が格納されている
func TestPanic(t *testing.T) {
defer func() {
if e := recover(); e == nil {
t.Fatal("no panic")
} else {
log.Printf("%+v", e)
}
}()
var i int
log.Println(1 / i)
}
go:embed
- 実行ファイルに他のファイルを埋め込める
- コンパイル時にパスを読み込み、実行時にバイト列として格納される
- 文字列やディレクトリも扱える
package main_test
import (
_ "embed"
"strings"
"testing"
)
//go:embed embed_test.go
var code string
func TestEmbed(t *testing.T) {
line := code[:strings.Index(code, "\n")]
if line != "package main_test" {
t.Error()
}
}
オプション引数
おもにコンストラクタの話
Functional Options Pattern
- 「structのポインタを引数とする関数」に別名をつける(Option)
- Optionを返す関数を作成する
- 設定内容に合わせ、
WithABC
のような名前にする - 引数
- 生成するstructのフィールドに設定する値
- 返り値
- 与えられたstructポインタのフィールドに値をセットする関数
- 設定内容に合わせ、
- コンストラクタ
- 可変長引数で複数の
Option
を受け取れるようにする - 与えられた
Option
をすべて実行し、生成するstructのフィールドをセットする
- 可変長引数で複数の
package main_test
import "testing"
type FOP struct {
a string
b string
}
type Option func(*FOP)
func WithB(s string) Option {
return func(a *FOP) {
a.b = s
}
}
func NewFOP(a string, opts ...Option) *FOP {
s := &FOP{a: a}
for _, opt := range opts {
opt(s)
}
return s
}
func TestFunctionalOptionsPattern(t *testing.T) {
a := NewFOP("a", WithB("b"))
if a.a != "a" {
t.Error("a")
}
if a.b != "b" {
t.Error("b")
}
}
Buidler Pattern
よくあるやつ
template
- FuncMapで自作関数を追加できる
- ParseFiles, ParseGlobでファイルからテンプレートを読み込める
-
go:embed
と組み合わせて使えそう
-
-
define
でテンプレートに名前をつけられる - 命令直後に
-
をつけると前後の改行を無効化できる-
{{- .Text -}}
-
ジェネレータの合流
package main_test
import (
"fmt"
"log"
"testing"
)
func generator(msg string) <-chan string {
ch := make(chan string)
go func() {
for i := 0; ; i++ {
ch <- fmt.Sprintf("%d: %s", i, msg)
}
}()
return ch
}
func join(generators ...<-chan string) <-chan string {
ch := make(chan string)
for _, g := range generators {
go func(g <-chan string) {
for {
ch <- <-g
}
}(g)
}
return ch
}
func TestJoin(t *testing.T) {
ch := join(
generator("a"),
generator("b"),
generator("c"),
generator("d"),
generator("e"),
)
for i := 0; i < 10; i++ {
s := <-ch
log.Println(s)
}
}
チャネルを使ったスロットリング
バッファのキャパシティサイズで同時実行数を制御する
func TestThrottling(t *testing.T) {
limit := make(chan struct{}, 5)
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
go func(i int) {
wg.Add(1)
defer wg.Done()
limit <- struct{}{}
log.Printf("no: %d", i)
time.Sleep(2 * time.Second)
<-limit
}(i)
}
time.Sleep(1 * time.Second)
if l := len(limit); l != 5 {
t.Error(l)
}
wg.Wait()
}
test
-
go test -short
で簡易なテストのみ実行する- オプションが有効かは
testing.Short()
で判別できる - オプションが有効な場合、
t.SkipNow()
で詳細なテストをスキップさせる
- オプションが有効かは
-
google/go-cmp
を使うと、差分を簡単に可視化できる -
t.Setenv
で一時的な環境変数をセットできる
このスクラップは6ヶ月前にクローズされました