Reputation: 3512
The goal is
Having a variable
B
that satisfies a specific interfaceI
through pointer receivers type, create another variableC
(with reflection and usingB
), copyB
's values intoC
, modifyC
(without changingB
) and returnC
as typeI
.
Suppose that I have the following types, the following snippets mimic production code:
import (
"reflect"
)
type IFace interface {
A()
B()
C()
}
type Meta struct {
s string
}
func (m *Meta) A() {}
func (m *Meta) B() {}
func (m *Meta) C() {}
type One struct {
M *Meta
B bool
}
func (o *One) A() {}
func (o *One) B() {}
func (o *One) C() {}
And I have a method that does the following:
func Alias(src, dest *Meta) (IFace, error) {
base, err := find(src) //asume that `find` is implemented and err is nil
if err != nil {
return err
}
// trouble starts here ...
// allocate new "instance"
aliased := reflect.New(reflect.TypeOf(base)).Elem().Interface()
// copy the base value
aliased = base
aliasedV := reflect.ValueOf(aliased).Elem()
fm := aliasedV.FieldByName("M")
fm.Set(reflect.ValueOf(dest))
return aliasedV.Interface().(Iface), nil
}
It compiles and runs however with the following TestFunction it gives me this error message:
interface conversion: One is not IFace: missing method C [recovered]
panic: interface conversion: One is not IFace: missing method C
and the test function:
func TestOne(t *testing.T) {
srcID := &Meta{S: "SRC"}
destID := &Meta{S: "DEST"}
aliased, err := Alias(srcID, destID)
if err != nil {
t.Error(err)
}
one, isOne := aliased.(*One)
if !isOne {
t.Error("fail")
}
}
Is there a way to have an interface{}
type that wraps a struct value become an interface{}
type that wraps a struct pointer without using the underlying struct type directly, like avoiding: var any interface{} = aliased.(*One)
??,
Could the unsafe
package be of help here?
Here is a playground that replicates the panic, thanks to RayfenWindspear
https://play.golang.org/p/860uAE7qLc
Upvotes: 1
Views: 1849
Reputation: 38203
Not sure if this is what you want, but if I understood you correctly, in your updated example from the comments the returned base
is what you want to copy, modify, and then return the copy of, without changing anything in base
. If that's the case, this code aliased = base
is not gonna do what you want if the base's underlying type is a pointer, which is true in this case.
Note, I've modified the var names to better reflect
your assignment.
// Having a variable B that satisfies a specific
// interface I through pointer receivers type.
B, err := find(src)
if err != nil {
return nil, err
}
// create another variable C (with reflection and using B),
C := reflect.New(reflect.TypeOf(B).Elem())
// copy B's values into C
bv := reflect.ValueOf(B).Elem()
for i :=0; i < bv.NumField(); i++ {
fv := bv.Field(i)
if fv.Kind() == reflect.Ptr {
v := reflect.New(fv.Elem().Type()) // allocate a new pointer of the same type as the B's pointer field is pointing to, in this case 'Meta'
v.Elem().Set(fv.Elem()) // set the newly allocated pointer's value to the same value as B's pointer field is pointing to, in this case 'Meta{S: "SRC"}'
C.Elem().Field(i).Set(v) // set the newly allocated pointer as the C's field
} else {
C.Elem().Field(i).Set(fv) // non pointer field? just set and that's it
}
// NOTE: if B's field's have subfields that are pointers you'll have to do this for all of them if you want a deep copy
}
// modify C (without changing B)
C.Elem().FieldByName("M").Elem().FieldByName("S").Set(reflect.ValueOf("Hello, 世界"))
Here's the playground: https://play.golang.org/p/bGTdy2vYUu
Sorry if this not what you're looking for.
Upvotes: 1