2014-04-13 8 views
13

Próbuję wprowadzić metodę, która zmienia wartość pól w obiekcie, który może mieć dowolną strukturę. Przemieszczanie pól nie stanowi problemu, gdy mam wskaźnik do struktury. Ale nie uda się zmienić pola, gdy mam interfejs, który nie owijać wskaźnik do struktury, ale sam struktury, w skrócie:Odbicie ginekologiczne: Nie można ustawić pól interfejsu owijającego struct

// The following doesn't work 
var x interface{} = A{Str: "Hello"} 
// This panics: reflect: call of reflect.Value.Field on ptr Value 
reflect.ValueOf(&x).Field(0).SetString("Bye") 
// This panics: reflect: call of reflect.Value.Field on interface Value 
reflect.ValueOf(&x).Elem().Field(0).SetString("Bye") 
// This panics: reflect: reflect.Value.SetString using unaddressable value 
reflect.ValueOf(&x).Elem().Elem().Field(0).SetString("Bye") 
// This prints `false`. But I want this to be settable 
fmt.Println(reflect.ValueOf(&x).Elem().Elem().Field(0).CanSet()) 

// This works 
var z interface{} = &A{Str: "Hello"} 
// This prints `true` 
fmt.Println(reflect.ValueOf(z).Elem().Field(0).CanSet()) 

W długi: http://play.golang.org/p/OsnCPvOx8F

Czytałem The Laws of Reflection więc jestem świadomy, że mogę modyfikować tylko pola, gdy mam wskaźnik do struct. Moje pytanie brzmi teraz: Jak uzyskać wskaźnik do danych struktury?

UPDATE:

mam to działa przy użyciu w zasadzie y := reflect.New(reflect.TypeOf(x)) więc wartości są ustawiane y teraz. Aby uzyskać obszerny przykład, zobacz: https://gist.github.com/hvoecking/10772475

Odpowiedz

8

Wygląda na to, że próbujesz zmodyfikować wartość dynamiczną przechowywaną wewnątrz zmiennej interfejsu. Jedyne operacje, które można wykonać na zmiennej interfejsowej, to pobranie lub ustawienie wartości dynamicznej (operacje wykonujące kopie) oraz sprawdzenie typu przechowywanej wartości.

Aby zrozumieć, dlaczego rzeczy są w ten sposób, sobie wyobrazić, że istnieje taka operacja i mieliśmy następujący kod:

var ptr *A = pointer_to_dynamic_value(x) 
x = B{...} 

Co ptr teraz reprezentować? Język ma możliwość ponownego użycia pamięci podczas przypisywania nowych wartości do zmiennej interfejsu, więc teraz ptr może teraz wskazywać pamięć dla wartości B, która łamie bezpieczeństwo typu tego języka (z bieżącym przechowywaniem kompilatorów jest gwarantowane tylko ponownie użyte dla małych wartości, ale punkt pozostaje).

Jedynym bezpiecznym sposobem na zmutowanie wartości przechowywanej w interfejsie jest skopiowanie wartości, a następnie przypisanie jej zmodyfikowanej wersji. Na przykład:

a := x.(A) 
a.Str = "Bye" 
x = a 

Pakiet reflect odzwierciedla te ograniczenia, więc reflect.Value reprezentujący dziedzinę wartości dynamicznego jest rozpatrywane tylko do odczytu.

Jesteś w stanie ustawić pola w pierwszym przykładzie, ponieważ dynamiczna wartość dla z jest wskaźnikiem *A, a nie samą strukturą: oznacza to, że odwoływana struktura może być modyfikowana.

+1

Ah, rozumiem. Moim sposobem obejścia tego problemu byłoby (w celu obsługi dowolnych struktur) utworzenie nowej instancji za pomocą funkcji 'y: = reflect.New (reflect.TypeOf (x))' i skopiowanie wszystkich pól 'x' do odpowiednie pola z 'y'. – Heye

+0

Dzięki Heye, uratowałeś mi kolejną godzinę poszukiwań. :) – Danyel

+0

To ma sens; ale jaki jest powód, dla którego nie można zmienić pola wewnątrz wartości dynamicznej, na przykład "x. (list) .head = z" - ponieważ nie tworzy to zwisającego wskaźnika. – WPWoodJr

Powiązane problemy