2016-08-05 30 views
14

Oto fragment kodu play.google.org który działa bez problemu:Eksportowanie funkcji z anonimową strukturą jako parametrem [nie można użyć wartości (typ struct {...}) jako typu struct {...} w argumencie package.Func]

package main 

import (
    "fmt" 
) 

func PrintAnonymous(v struct { 
    i int 
    s string 
}) { 
    fmt.Printf("%d: %s\n", v.i, v.s) 
} 

func PrintAnonymous2(v struct{}) { 
    fmt.Println("Whatever") 
} 

func main() { 
    value := struct { 
     i int 
     s string 
    }{ 
     0, "Hello, world!", 
    } 
    PrintAnonymous(value) 
    PrintAnonymous2(struct{}{}) 
} 

jednak jeśli funkcja PrintAnonymous() istnieje w innym opakowaniu (powiedzmy, temp), kod nie zadziała:

cannot use value (type struct { i int; s string }) 
as type struct { i int; s string } in argument to temp.PrintAnonymous 

Moje pytanie to:

  • Czy istnieje sposób wywołania funkcji (publicznej) z anonimową strukturą jako parametrem (a.k.a. PrintAnonymous() powyżej)?
  • Funkcja z pustą strukturą jako parametrem (a.k.a. PrintAnonymous2() powyżej) może zostać wywołana, nawet jeśli istnieje w innym pakiecie. Czy to szczególny przypadek?

Cóż, wiem, że zawsze mogę wymienić struct aby rozwiązać problem, jestem tylko ciekaw o tym, i zastanawiam się, dlaczego wydaje się, że nie jest to dozwolone.

+0

Co się stanie, jeśli wielkie litery będą miały właściwości struct? struct {I int S string} – Dale

+0

@Dalej: Tak! Właśnie to zauważyłem! Dzięki! Jak głupio jestem! –

Odpowiedz

8

Pola swojej anonimowej typu struct są unexported. Oznacza to, że nie można tworzyć wartości tej struktury i określać wartości dla pól z innego pakietu. Spec: Composite literals:

Błędem jest podanie elementu dla niewyeksportowanego pola struktury należącego do innego pakietu.

Jeśli zmienisz definicję struktury, aby wyeksportować pola, to zadziała, ponieważ wszystkie pola mogą być przypisane do innych pakietów (patrz Siu Ching Pong - odpowiedź Assuki Kenji).

Jest tak również w przypadku pustej struktury (bez pól): pusta struktura nie ma pól, więc nie ma nieodportowanych pól, więc możesz przekazać jej wartość.

Możesz jednak wywołać funkcję z niezmodyfikowaną strukturą (z nieodportowanymi polami) przez reflection. Możesz uzyskać reflect.Type funkcji PrintAnonymous() i możesz użyć Type.In(), aby uzyskać reflect.Type pierwszego parametru. To jest anonimowa struktura, dla której chcemy przekazać wartość. I możesz skonstruować wartość tego typu za pomocą reflect.New(). Będzie to reflect.Value, zawijanie wskaźnika do zero value anonimowej struktury. Niestety, nie możesz mieć wartości struct z polami o wartościach niezerowych (z powodów wymienionych powyżej).

ten sposób mogłoby to wyglądać tak:

v := reflect.ValueOf(somepackage.PrintAnonymous) 
paramt := v.Type().In(0) 
v.Call([]reflect.Value{reflect.New(paramt).Elem()}) 

ten wypisze:

0: 

0 jest zerowa wartość dla int i "" pusty ciąg dla string.

Dla głębiej do systemu typu i kodowanym z polami unexported, zobaczyć inne pytania:

Identify non builtin-types using reflect
How to clone a structure with unexported field?


ciekawe (jest to błąd zobacz połączony problem poniżej), używając refleksji, możesz użyć wartości własnego anonimowego typu struktury (z dopasowaniem, Pola unexported), w tym przypadku możemy użyć wartości inne niż wartość zerową pól struct:

value := struct { 
    i int 
    s string 
}{ 
    1, "Hello, world!", 
} 

v.Call([]reflect.Value{reflect.ValueOf(value)}) 

Wyżej przebiegów (bez paniki):

1: Hello, world! 

Powodem jest to dozwolone jest z powodu błędu w kompilatorze. Zobacz przykładowy kod poniżej:

s := struct{ i int }{2} 

t := reflect.TypeOf(s) 
fmt.Printf("Name: %q, PkgPath: %q\n", t.Name(), t.PkgPath()) 
fmt.Printf("Name: %q, PkgPath: %q\n", t.Field(0).Name, t.Field(0).PkgPath) 

t2 := reflect.TypeOf(subplay.PrintAnonymous).In(0) 
fmt.Printf("Name: %q, PkgPath: %q\n", t2.Name(), t2.PkgPath()) 
fmt.Printf("Name: %q, PkgPath: %q\n", t2.Field(0).Name, t2.Field(0).PkgPath) 

wyjścia:

Name: "", PkgPath: "" 
Name: "i", PkgPath: "main" 
Name: "", PkgPath: "" 
Name: "i", PkgPath: "main" 

Jak widać pole unexported i w obu anonimowych typów struct (w main opakowaniu iw somepackage jako parametr do PrintAnonymous() funkcyjnego) - fałszywie - zgłoś to samo opakowanie, a tym samym ich typ będzie równy:

fmt.Println(t == t2) // Prints true 

Uwaga: uważam to za skaza: jeśli pozwala na to odbicie, powinno to być możliwe bez użycia refleksji. Jeśli bez refleksji błąd kompilacji jest uzasadniony, użycie refleksji powinno spowodować panikę w czasie wykonywania. Otworzyłem problem, możesz to zrobić tutaj: issue #16616. Fix aktualnie ma cel Go 1.8.

+0

Ładne informacje! Dzięki! –

4

Och! Właśnie zdałem sobie sprawę, że nazwy pól są pisane małymi literami, a zatem nie są publiczne! Zmiana pierwszą literę nazwy pól na wielkie rozwiązuje problem:

package main 

import (
    "temp" 
) 

func main() { 
    value := struct { 
     I int 
     S string 
    }{ 
     0, "Hello, world!", 
    } 
    temp.PrintAnonymous(value) 
    temp.PrintAnonymous2(struct{}{}) 
} 
package temp 

import (
    "fmt" 
) 

func PrintAnonymous(v struct{I int; S string}) { 
    fmt.Printf("%d: %s\n", v.I, v.S) 
} 

func PrintAnonymous2(v struct{}) { 
    fmt.Println("Whatever") 
}