Arabirim arabirimlerini dönüştürme dilimleri yazın


194

Git does't örtük dönüştürme neden merak ediyorum []Tiçin []interface{}dolaylı dönüştürür zaman Tiçin interface{}. Kaçırdığım bu dönüşümle ilgili önemsiz olmayan bir şey var mı?

Misal:

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

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

go build şikayet

işlev bağımsız değişkeninde tür [] arabirimi {} olarak bir (type [] dizesi) kullanılamaz

Ve açıkça yapmaya çalışırsam, aynı şey: b := []interface{}(a)şikayet ediyor

(type [] dizesi) [] arayüzüne {} dönüştürülemiyor

Yani (bu çok geliyor gibi görünüyor) bu dönüşüm yapmak her zaman, ben böyle bir şey yapıyorum:

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

Bunu yapmanın daha iyi bir yolu veya bu dönüşümlere yardımcı olacak standart kitaplık işlevleri var mı? Örneğin, ints veya dizelerin bir listesini alabilen bir işlevi çağırmak istediğinizde her zaman 4 ekstra kod satırı yazmak aptalca görünüyor.


"yeni bir dilim tahsis etmek ve tüm öğeleri kopyalamak" anlamına gelen "[] ​​T'yi [] arayüzüne {}" dolaylı olarak dönüştürün. Bu, "dolaylı olarak T'yi {}" arabirimine dolaylı olarak dönüştüren şeyle tutarsızdır; bu yalnızca daha genel bir statik türle aynı değerin bir görünümüdür; hiçbir şey kopyalanmaz; ve tekrar T türüne geri yazarsanız, aynı şeyi geri alırsınız.
newacct

1
nasıl uygulanacağı hakkında çok ayrıntılı düşünmüyordum, sadece daha yüksek bir seviyede genel bir listeye sahip olmamak için bir geçici çözüm olarak bir listenin tamamını başka bir türe kolayca dönüştürmek uygun olurdu.
danny

Yanıtlar:


215

Go'da sözdiziminin karmaşık / maliyetli işlemleri gizlememesi gerektiğine dair genel bir kural vardır. Bir çevrilmesi string, bir üzere interface{}O (1) bir sürede gerçekleştirilir. Bir çevrilmesi []string, bir için interface{}bir dilim yine bir değer olduğu da O (1) zaman yapılır. Bununla birlikte, bir dönüştürme []stringbir için []interface{}dilimin her bir eleman bir dönüştürülmesi gerekir, çünkü O (n) zamanı interface{}.

Bu kuralın tek istisnası dizeleri dönüştürmektir. Bir dönüştürürken stringve bir gelen []byteveya []runeGo dönüşümler "sözdizimi" olsa bile O (n) çalışır.

Bu dönüşümü sizin için yapacak standart bir kütüphane işlevi yoktur. Yansıtma ile bir tane yapabilirsiniz, ancak üç satır seçeneğinden daha yavaş olacaktır.

Yansımalı örnek:

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

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

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

    return ret
}

En iyi seçeneğiniz, yalnızca sorunuzda verdiğiniz kod satırlarını kullanmaktır:

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

3
daha yavaş olabilir, ancak herhangi bir dilim ile genel olarak çalışır
newacct

Bütün cevap mapçok btw için geçerlidir .
RickyA

Bu aynı zamanda geçerlidir channels.
Justin Ohms

Açık bir açıklama için teşekkürler, mümkünse ref ekleyebilirsiniz. için Converting a []string to an interface{} is also done in O(1) time?
Mayur

56

Eğer eksik bir şey olduğunu Tve interface{}değerini tutan Tbellekte farklı gösterimleri yüzden trivially dönüştürülemez var.

Bir tür değişkeni Tsadece bellekteki değeridir. İlişkili tür bilgisi yoktur (Go'da her değişkenin derleme zamanında bilinen tek bir türü vardır). Hafızada şu şekilde temsil edilir:

  • değer

Bir interface{}tip tutma değişken Tböyle bellekte temsil edilir

  • yazı tipi T
  • değer

Yani asıl soruya geri geliyor: neden go does't örtülü dönüştürmek []Tiçin []interface{}?

Dönüştürme []Tiçin []interface{}yeni bir kesik yaratan yer alacağı interface {}bellek düzeni tamamen farklı olduğu için bu önemsiz olmayan bir işlemdir değerleri.


10
Bu bilgilendirici ve iyi yazılmış (+1), ancak sorusunun diğer kısmını ele almıyorsunuz: "Bunu yapmanın daha iyi bir yolu var mı ..." (-1).
weberc2


8

interface{}Bunun yerine deneyin . Dilim olarak geri almak için şunu deneyin:

func foo(bar interface{}) {
    s := bar.([]string)
    // ...
}

3
Bu bir sonraki soruya yalvarır: OP'nin baronu "herhangi bir türden dilim" olarak yorumlamak için nasıl yinelemesi gerekiyor ? Üç astarının başka bir beton türünde a []interface{}, değil []stringveya bir dilim oluşturduğunu unutmayın.
kostix

14
-1: Bu yalnızca bar [] func foo(bar []string) { /* ... */ }
dizgiyse

2
bir tip anahtarı kullanın veya kabul edilen cevaptaki yansımayı kullanın.
dskinner

OP herhangi bir şekilde çalışma zamanında türünü bulmak zorunda kalacak. Dilimsiz kullanılması interface{}, derleyiciyi olduğu gibi bir argüman iletirken şikayet etmeyecektir foo(a). İçinde yansıtma veya tip anahtarlama ( golang.org/doc/effective_go.html#type_switch ) kullanabilirfoo .
Cassiohg

2

interface{}Herhangi bir türe dönüştürün .

Sözdizimi:

result := interface.(datatype)

Misal:

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

2
Bundan emin misin? Bunun geçerli olduğunu sanmıyorum. Göreceksin: invalid type assertion: potato.([]string) (non-interface type []interface {} on left)
Adriano Tadao

2

Kodunuzu kısaltmanız gerektiğinde yardımcı için yeni bir tür oluşturabilirsiniz

type Strings []string

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

sonra

a := []strings{"a", "b", "c", "d"}
sliceIFace := Strings(a).ToInterfaceSlice()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.