0
0

More than 1 year has passed since last update.

Go言語で配列・スライスをなんでも受け取れる関数を作る

Posted at

はじめに

[]int でも []string でも、どのような型の配列でも、どのような型のスライスでも、引数として受け取れる関数を作りたいことがありました。
作ろうとしたら一筋縄ではいかなかったので、その方法を書き留めておきます。

環境

terminal
% go version
go version go1.17.6 darwin/amd64

失敗例

はじめに、引数の型に interface{} を使うとなんでも受け取れるので []interface{} を使えばどのような型の配列・スライスでも受け取れるだろうと思い、以下のコードを書きました。

main.go
package main

import (
    "fmt"
)

func printElements(array []interface{}) { // []int や []string など、配列・スライスならなんでも受け取りたい
    for _, e := range array {
        fmt.Println(e)
    }
}

func main() {
    numbers := []int{2, 3, 5, 7, 11}
    printElements(numbers)

    texts := []string{"foo", "bar", "baz", "qux", "quux"}
    printElements(texts)
}
実行結果
./main.go:15:15: cannot use numbers (type []int) as type []interface {} in argument to printElements
./main.go:18:15: cannot use texts (type []string) as type []interface {} in argument to printElements

しかし、これを実行しようとするとコンパイルに失敗します。
どうやら []interface{} 型の引数は interface{} 型の配列・スライスしか受け取れないようです。

解決方法

やっていることの本質は同じですが、一応2パターンあるので分けて記載します。

引数の型に interface{} を使用する

どのような型の配列・スライスでも受け取れるように、引数の型をなんでも受け取れる interface{} にします。
そして、リフレクションを使用して引数の値を判別し、配列またはスライスでない場合はエラーを返すようにします。

main.go
package main

import (
    "fmt"
    "reflect"
)

func printElements(array interface{}) error {
    arr := reflect.ValueOf(array)
    if arr.Kind() != reflect.Array && arr.Kind() != reflect.Slice {
        return fmt.Errorf(`"%#v" is not array`, array)
    }

    for i := 0; i < arr.Len(); i++ {
        fmt.Println(arr.Index(i))
    }

    return nil
}

func main() {
    numbers := []int{2, 3, 5, 7, 11}
    _ = printElements(numbers)

    texts := []string{"foo", "bar", "baz", "qux", "quux"}
    _ = printElements(texts)
}
実行結果
2
3
5
7
11
foo
bar
baz
qux
quux

配列・スライスを []interface{} 型に変換する

まず、どのような型の配列・スライスでも []interface{} 型に変換する関数を用意します。
この関数はリフレクションを使用して引数の値を判別し、配列またはスライスでない場合にエラーを返します。
そうしたら、行いたい処理は []interface{} 型を引数にとる関数として実装します。

main.go
package main

import (
    "fmt"
    "reflect"
)

func convert(array interface{}) ([]interface{}, error) {
    arr := reflect.ValueOf(array)
    if arr.Kind() != reflect.Array && arr.Kind() != reflect.Slice {
        return nil, fmt.Errorf(`"%#v" is not array`, array)
    }

    res := make([]interface{}, arr.Len())
    for i := 0; i < arr.Len(); i++ {
        res[i] = arr.Index(i).Interface()
    }

    return res, nil
}

func printElements(array []interface{}) {
    for _, e := range array {
        fmt.Println(e)
    }
}

func main() {
    numbers := []int{2, 3, 5, 7, 11}
    ns, _ := convert(numbers)
    printElements(ns)

    texts := []string{"foo", "bar", "baz", "qux", "quux"}
    ts, _ := convert(texts)
    printElements(ts)
}
実行結果
2
3
5
7
11
foo
bar
baz
qux
quux

おわりに

これで便利にどんな配列・スライスでも扱えますね。
リフレクション様様です。

関連

  1. golangのinterface{}の不思議
  2. 【Go言語】[]string, []int to []interface{} とかそういうの
  3. Go言語 スライスのchunk関数をつくる
0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0