269 votes

Type de conversion des tranches d'interfaces

Je suis curieux de savoir pourquoi Go ne convertit pas implicitement []T a []interface{} alors qu'il convertira implicitement T a interface{} . Y a-t-il quelque chose de non trivial dans cette conversion qui m'échappe ?

Exemple :

func foo([]interface{}) { /* do something */ }

func main() {
    var a []string = []string{"hello", "world"}
    foo(a)
}

go build se plaint

ne peut pas utiliser un (type []string) comme type []interface {} dans l'argument d'une fonction

Et si j'essaie de le faire explicitement, c'est la même chose : b := []interface{}(a) se plaint

ne peut pas convertir un (type []string) en type []interface {}

Ainsi, chaque fois que j'ai besoin d'effectuer cette conversion (ce qui semble se produire souvent), j'ai procédé de la manière suivante :

b = make([]interface{}, len(a), len(a))
for i := range a {
    b[i] = a[i]
}

Existe-t-il une meilleure façon de procéder, ou des fonctions de la bibliothèque standard pour faciliter ces conversions ? Il me semble un peu idiot d'écrire 4 lignes de code supplémentaires à chaque fois que je veux appeler une fonction qui peut prendre une liste d'ints ou de chaînes, par exemple.

4voto

angelsages Points 117

Si vous souhaitez raccourcir davantage votre code, vous pouvez créer un nouveau type d'aide

type Strings []string

func (ss Strings) ToInterfaceSlice() []interface{} {
    iface := make([]interface{}, len(ss))
    for i := range ss {
        iface[i] = ss[i]
    }
    return iface
}

puis

a := []strings{"a", "b", "c", "d"}
sliceIFace := Strings(a).ToInterfaceSlice()

2voto

Nikita Sokolsky Points 807

J'étais curieux de savoir à quel point il est plus lent de convertir des tableaux en interface par réflexion que de le faire à l'intérieur d'une boucle, comme décrit dans Réponse de Stephen . Voici une comparaison des deux approches :

benchmark                             iter      time/iter   bytes alloc         allocs
---------                             ----      ---------   -----------         ------
BenchmarkLoopConversion-12         2285820   522.30 ns/op      400 B/op   11 allocs/op
BenchmarkReflectionConversion-12   1780002   669.00 ns/op      584 B/op   13 allocs/op

L'utilisation d'une boucle est donc ~20% plus rapide que de le faire par réflexion.

Voici mon code de test au cas où vous voudriez vérifier si j'ai fait les choses correctement :

    import (
        "math/rand"
        "reflect"
        "testing"
        "time"
    )

    func InterfaceSlice(slice interface{}) []interface{} {
        s := reflect.ValueOf(slice)
        if s.Kind() != reflect.Slice {
            panic("InterfaceSlice() given a non-slice type")
        }

        // Keep the distinction between nil and empty slice input
        if s.IsNil() {
            return nil
        }

        ret := make([]interface{}, s.Len())

        for i := 0; i < s.Len(); i++ {
            ret[i] = s.Index(i).Interface()
        }

        return ret
    }

    type TestStruct struct {
        name string
        age  int
    }

    var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

    func randSeq(n int) string {
        b := make([]rune, n)
        for i := range b {
            b[i] = letters[rand.Intn(len(letters))]
        }
        return string(b)
    }

    func randTestStruct(lenArray int, lenMap int) map[int][]TestStruct {
        randomStructMap := make(map[int][]TestStruct, lenMap)
        for i := 0; i < lenMap; i++ {
            var testStructs = make([]TestStruct, 0)
            for k := 0; k < lenArray; k++ {
                rand.Seed(time.Now().UnixNano())
                randomString := randSeq(10)
                randomInt := rand.Intn(100)
                testStructs = append(testStructs, TestStruct{name: randomString, age: randomInt})
            }
            randomStructMap[i] = testStructs
        }
        return randomStructMap
    }

    func BenchmarkLoopConversion(b *testing.B) {
        var testStructMap = randTestStruct(10, 100)
        b.ResetTimer()

        for i := 0; i < b.N; i++ {
            obj := make([]interface{}, len(testStructMap[i%100]))
            for k := range testStructMap[i%100] {
                obj[k] = testStructMap[i%100][k]
            }
        }
    }

    func BenchmarkReflectionConversion(b *testing.B) {
        var testStructMap = randTestStruct(10, 100)
        b.ResetTimer()

        for i := 0; i < b.N; i++ {
            obj := make([]interface{}, len(testStructMap[i%100]))
            obj = InterfaceSlice(testStructMap[i%100])
            _ = obj
        }
    }

-3voto

yala ramesh Points 721

Convertir interface{} dans n'importe quel type.

Syntaxe :

result := interface.(datatype)

Exemple :

var employee interface{} = []string{"Jhon", "Arya"}
result := employee.([]string)   //result type is []string.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X