Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to copy an interface value in Go?

How to copy an interface value in Go?

My User interface:

type User interface {
    Name() string
    SetName(name string)
}

My Admin struct:

type Admin struct {
    name string
}

func (a *Admin) Name() string {
    return a.name
}

func (a *Admin) SetName(name string) {
    a.name = name
}

I try to copy user1's value.

Main Function:

func main() {
    var user1 User
    user1 = &Admin{name:"user1"}

    fmt.Printf("User1's name: %s\n", user1.Name())

    var user2 User
    user2 = user1
    user2.SetName("user2")

    fmt.Printf("User2's name: %s\n", user2.Name()) // The name will be changed as "user2"
    fmt.Printf("User1's name: %s\n", user1.Name())  // The name will be changed as "user2" too, How to make the user1 name does not change?
}

How to achieve that changing the copy's name the original doesn't change?

like image 920
HeadwindFly Avatar asked Jun 16 '16 06:06

HeadwindFly


1 Answers

The problem here is that your user1 variable (which is of type User) holds a pointer to an Admin struct.

When you assign user1 to another variable (of type User), the interface value which is a pair of the dynamic type and value (value;type) will be copied - so the pointer will be copied which will point to the same Admin struct. So you only have one Admin struct value, both user1 and user2 refer (point) to this. Changing it through any of the interface values changes the one and only value.

To make user1 and user2 distinct, you need 2 "underlying" Admin structs.

One way is to type assert the value in the user1 interface value, and make a copy of that struct, and wrap its address in another User value:

var user2 User
padmin := user1.(*Admin) // Obtain *Admin pointer
admin2 := *padmin        // Make a copy of the Admin struct
user2 = &admin2          // Wrap its address in another User
user2.SetName("user2")

Now they will be distinct, output (try it on the Go Playground):

User1's name: user1
User2's name: user2
User1's name: user1

Of course this solution has its limitation: the dynamic type stored in the User interface value is "wired" in the solution (*Admin).

Using reflection

If we want a "general" solution (not just one that works with *Admin), we can use reflection (reflect package).

For simplicity let's assume user1 always contains a pointer (for now).

Using reflection we can get the dynamic type (here *Admin), and even the dynamic type without a pointer (Admin). And we can use reflect.New() to obtain a pointer to a new value of that type (whose type will be identical to the original dynamic type in user1 - *Admin), and wrap this back into a User. This is how it could look like:

var user3 User
user3 = reflect.New(reflect.ValueOf(user1).Elem().Type()).Interface().(User)
user3.SetName("user3")

Output (try this one on the Go Playground):

User1's name: user1
User3's name: user3
User1's name: user1

Note that reflect.New() will create a new value which is initialized to its zero value (so it will not be a copy of the original). It's not a problem here as Admin only has one field which we're about to change anyway, but must be kept on our mind in general.

Our initial assumption was that user1 contains a pointer. Now the "full" solution must not make such assumption. If the value in user1 would not be a pointer, this is how it could be "cloned":

var user3 User
if reflect.TypeOf(user1).Kind() == reflect.Ptr {
    // Pointer:
    user3 = reflect.New(reflect.ValueOf(user1).Elem().Type()).Interface().(User)
} else {
    // Not pointer:
    user3 = reflect.New(reflect.TypeOf(user1)).Elem().Interface().(User)
}
user3.SetName("user3")
like image 67
icza Avatar answered Oct 12 '22 13:10

icza