Programmingバックエンド開発者

Goにおける可変引数関数の実装方法について説明してください。正しく宣言し、呼び出す方法、スライスを渡す際の注意点などは何ですか?

Hintsage AIアシスタントで面接を突破

回答

Goの可変引数関数は、スリーピリオド構文 ... を使用することで、同じ型の可変数の引数を受け取ることを可能にします。例えば:

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

このような関数は任意の数の引数を持つことができます:

result := sum(1, 2, 3, 4) // 10

または、スライスを ... 構文を使用して渡すこともできます:

numbers := []int{1,2,3} result := sum(numbers...) // 6

コツ:

  • 可変引数は関数のパラメータとしてスライス([]T)として表示されます。これは、あらかじめ用意されたスライスを内部に渡すことができることを意味します。
  • こうした引数は常に関数のパラメータリストの最後である必要があります。
  • スライスを ... なしで渡すと、関数は通常のパラメータである []T を期待し、可変引数としては扱われません。

トリッキーな質問

次のコードはどのような結果を出し、なぜですか?

func printInts(nums ...int) { fmt.Printf("%#v ", nums) } ints := []int{1, 2, 3} printInts(ints)

多くの人はこのコードがコンパイルされると誤解していますが、実際にはコンパイルエラーが発生します:

エラー: 可変引数にスライスを ... なしで渡すことはできません。

正しい方法:

printInts(ints...) // OK

このトピックの微妙な知識による実際のエラー例


大規模なプロジェクトで、ログ出力関数を func Log(msgs ...string) として定義しました。事前に用意された文字列のスライスを渡す際に Log(strings) と呼び出し、ログ出力が不正になったり、実行時にpanicが発生しました。原因は ... が欠けていたため、関数は1つの引数 []string を受け取ると見なしていたからです。


あるユーティリティ関数では、スライスを含む引数を処理し、その内部でさらに要素を追加する必要がありました。... がスライスのコピーを作成すると期待していましたが、実際には参照を渡していたため、関数の内部でスライスが変更され、呼び出し側に予期しない影響を与えました。特に元のスライスが再利用可能なバッファである場合に問題が顕著になりました。


開発者は可変引数を使用して集約関数を実装しましたが、同僚の一人がキャスティングを介して呼び出す構文を誤って使用しました: sum(interface{}([]int))。これが時間帯の不明なエラーを引き起こしましたが、正しくは明示的に sum(slice...) を使用すべきでした。微妙な違いに対する知識の欠如は、大規模なリファクタリング時に明らかでないバグを引き起こしました。