#20 Goで学ぶ関数の応用(デフォルト引数・可変長引数)の基本

Pythonを触ったことがある人なら、関数を定義するときに「引数を省略したらこの値を使う」というデフォルト引数を書いた経験があるかもしれません。ところが、Goで同じ書き方をしようとすると、そんな構文はどこにもないことに気づきます。今回は、Goに「デフォルト引数」がない理由と、その代わりにどう書けばよいのかを紹介します。あわせて、渡す個数を自由に変えられる「可変長引数」についても解説します。

Goにはデフォルト引数がない

PythonやJavaScript、C++などでは、関数の引数に初期値をあらかじめ設定しておく「デフォルト引数」という仕組みがあります。呼び出し側が値を渡さなければ、その初期値が自動的に使われる、というものです。

ところがGoの設計方針は「言語仕様をできるだけシンプルに保つ」ことを重視しており、デフォルト引数の構文はあえて用意されていません。同じように、同じ関数名で引数の型や数が違うバージョンを複数定義できる「関数オーバーロード」もGoには存在しません。慣れないうちは不便に感じるかもしれませんが、これはGoが「関数を読んだときに、何が起きるか一目でわかること」を大事にしているためです。

可変長引数(...構文)の使い方

デフォルト引数はありませんが、「引数の個数を呼び出し側が自由に決められる」可変長引数はGoにもちゃんと用意されています。型の前に...を付けるだけです。

func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

func main() {
    fmt.Println(sum())        // 0
    fmt.Println(sum(1, 2))    // 3
    fmt.Println(sum(1, 2, 3)) // 6
}

関数の中では、numsはint型のスライス(可変長の配列のようなもの)として扱われます。引数を1つも渡さなければ空のスライスになるため、上のようにsum()を呼んでもエラーにはなりません。標準ライブラリのfmt.Printlnも、実はこの可変長引数を使って実装されています。

すでにあるスライスを渡したいとき

可変長引数の関数に、すでに用意したスライスをそのまま渡したい場合は、変数の後ろに...を付けます。

nums := []int{4, 5, 6}
fmt.Println(sum(nums...)) // 15

デフォルト引数の代わりになる書き方

では「引数を省略したら初期値を使いたい」場合、Goではどうするのでしょうか。よく使われる方法を2つ紹介します。

1. ゼロ値をデフォルトとして扱う

Goの変数には、宣言しただけで自動的に入る「ゼロ値」があります(数値なら0、文字列なら空文字、といった具合です)。これを利用して、関数の内部で「値が渡されなかった(=ゼロ値のまま)場合はこの値を使う」と判定する方法があります。

func greet(name string) string {
    if name == "" {
        name = "ゲスト"
    }
    return "こんにちは、" + name + "さん"
}

2. オプション構造体パターン

設定項目が多い関数では、引数をひとつずつ並べる代わりに「設定をまとめた構造体(複数のデータをひとまとめにした型)」を渡す方法がよく使われます。呼び出し側は必要な項目だけを指定できるので、デフォルト引数に近い使い勝手になります。

type ServerOptions struct {
    Port    int
    Timeout int
}

func NewServer(opts ServerOptions) {
    if opts.Port == 0 {
        opts.Port = 8080 // デフォルト値
    }
    if opts.Timeout == 0 {
        opts.Timeout = 30
    }
    // ここでサーバーを起動する処理を書く
}

func main() {
    NewServer(ServerOptions{Port: 3000}) // Timeoutは自動で30になる
}

この書き方は「オプション構造体パターン」と呼ばれ、Goの実務コードでもよく見かけます。関数オーバーロードが使えない代わりに、こうした構造体や可変長引数を組み合わせて柔軟性を確保する、というのがGoらしい発想です。

まとめ

Goには、他の言語にあるデフォルト引数や関数オーバーロードの構文がありません。しかしそれは機能が足りないというより、「関数の挙動を呼び出し側からも読みやすくする」というGoの設計思想の表れです。可変長引数の...構文や、ゼロ値・オプション構造体パターンを使いこなせば、他言語のデフォルト引数と同じような柔軟さを実現できます。最初は少し遠回りに感じるかもしれませんが、慣れてくるとコードの意図が読み取りやすくなるメリットを実感できるはずです。

関連記事


本ページはプロモーションを含みます。

参考書籍(PR)

Goをもっと学びたい方には「Go言語入門」がおすすめです。

コメント

このブログの人気の投稿

#25 Rustで学ぶAI API(ChatGPT等)の呼び出し方の基本

#8 C++で学ぶ文字列操作の基本

#16 TypeScriptで学ぶ辞書型(マップ)の基本