New関数の慣習
不正な状態を作れなくする
構造体はどんな値でも入れられます。
Go
item := Item{Name: "", Price: -500} // 通ってしまう名前が空で価格が負の商品ができました。エラーにはなりません。この Item が後の処理に流れていくと、どこかで壊れます。
作る時点で止めます。 そのための関数を用意するのが Go の慣習です。
Go
func NewItem(name string, price int) (*Item, error) {
if name == "" {
return nil, errors.New("商品名が空です")
}
if price <= 0 {
return nil, errors.New("価格が0以下です")
}
return &Item{Name: name, Price: price}, nil
}第4章でやった検証が、生成と一体になりました。
New という名前
Go には constructor という仕組みがありません。ただの関数です。ただし名前の付け方に決まりがあります。
プレーンテキスト
NewItem Item を作る
NewUser User を作る
New パッケージの主役の型を作るNew で始めるだけで、読む人には「これは生成関数だ」と伝わります。言語の機能ではなく、名前の習慣で揃えているのが Go らしいところです。
&Item{} が返るもの
Go
return &Item{Name: name, Price: price}, nil& は「その居場所」を意味します。前のレッスンの *Item と対になっていて、&Item{...} は「作った Item の居場所」を返します。
プレーンテキスト
*Item 居場所を表す型
&item 居場所を取り出す生成関数がポインタを返すのは、受け取った側がメソッドで書き換えられるようにするためです。値で返すと、前のレッスンの問題がそのまま起きます。
失敗したときは nil
Go
return nil, errors.New("商品名が空です")ポインタのゼロ値は nil です。第4章で (int, error) を返したときの 0 にあたるものが、ポインタでは nil になります。
受け取る側は、いつもの形で確かめます。
Go
item, err := NewItem("", 1200)
if err != nil {
return err.Error()
}
// ここから先、item は nil ではないと信用してよいerr を確かめずに item を使うと panic します。 nil のポインタからフィールドを読もうとするからです。第4章で「エラーを _ で捨てるな」と書いた理由が、ここでは実行時の停止という形で現れます。
要件
NewItemは(*Item, error)を返す- 検証の順番は 商品名が空 → 価格が0以下 → 価格が上限超え
- 成功時は
&Item{...}とnilを返す - 失敗時は
nilとエラーを返す createItemは成功なら商品名 価格円 を作成しました、失敗ならエラーの理由を返す
入出力例
createItem("きのこ図鑑", 1200) → "きのこ図鑑 1200円 を作成しました"
createItem("", 1200) → "商品名が空です"
createItem("きのこ図鑑", 0) → "価格が0以下です"
createItem("高級品", 2000000) → "価格が上限を超えています"
createItem("高級品", 1000000) → "高級品 1000000円 を作成しました"ヒント
編集 ゆめさく編集部