Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy or New instance of an interface

Tags:

go

I have a function that takes an interface and returns an interface. It would like to initialize the result to a copy of the source, then make some changes, and return the result. For example:

Playground

type Something interface {
    CopySomething() Something  // I'd like to get rid of this
    SetX(x int)
}

type RealThing struct {
    x int
}

func (t *RealThing) SetX(x int) {
    t.x = x
}

func (t *RealThing) CopySomething() Something {
    newT := *t
    return &newT
}

func Updated(original Something, newX int) Something {
    newThing := original.CopySomething()  // I'd like to make the copy without .CopySomething()
    newThing.SetX(newX)
    return newThing
}

func main() {
    a := &RealThing{x: 1}
    b := Updated(a, 5)
    fmt.Printf("a = %v\n", a)
    fmt.Printf("b = %v\n", b)
}

This works, but the CopySomething() method seems unnecessary (and I'd need another identical method for every interface that needs to copy things). Is there a better way to make a copy of original inside of Updated() without an extra method? Is there some more idiomatic way to achieve this?

In the specific case I'm working on, I could get away with just instantiating a new instance of the same type as original (I don't really need the copy). Is the problem any simpler that way?


Based on Evan's comment, I thought I'd give some of the other reflect-based things I've already tried:

newThing := reflect.New(reflect.TypeOf(original))

==> Compile error: type reflect.Value has no field or method SetX

newThing := reflect.New(reflect.TypeOf(original)).Interface().(Something)

===> Runtime error: interface conversion: **main.RealThing is not main.Something

newThing := reflect.Indirect(reflect.New(reflect.TypeOf(original))).Interface().(Something)

===> Runtime error: invalid memory address or nil pointer dereference

At this point, I felt my reflect was becoming silly and stopped just whacking at it.

like image 469
Rob Napier Avatar asked Jan 11 '23 20:01

Rob Napier


2 Answers

Since you just need to instantiate a new instance you can use reflection to get the type of the object stored in the interface and instantiate a copy that way. Something like reflect.New(reflect.TypeOf(x)) though you may have to play with reflect.Indirect() in order to allocate a new value and not a new pointer.

It's all documented here: http://golang.org/pkg/reflect/

And a runnable version: http://play.golang.org/p/z8VPzDKrSk

like image 180
Evan Avatar answered Jan 30 '23 19:01

Evan


newThing := reflect.New(reflect.TypeOf(original))

==> Compile error: type reflect.Value has no field or method SetX

If original is an pointer is need get element:

elem := reflect.TypeOf(original).Elem()    
newThing := reflect.New(elem)

newThing := reflect.New(reflect.TypeOf(original)).Interface().(Something)

===> Runtime error: interface conversion: **main.RealThing is not main.Something

Fixing and continuing... Knowing that newThing is pointer for an new RealThing element, then get this pointer by .Interface(), but on interface{}.

elem := reflect.TypeOf(original).Elem()    
newThingValue := reflect.New(elem)
newThingInterf := newThingValue.Interface()

For get pointer direct to RealThing is need do assertion, then

newThing := newThingInterface.(*RealThing)

So you can update newThing normally

newThing.SetX(newX)//update new instance

See working in https://play.golang.org/p/N-pwH5J_el

like image 38
Aristofanio Garcia Avatar answered Jan 30 '23 19:01

Aristofanio Garcia