How to copy an interface value in Go?
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")
Another solution could be to add a Clone() method to your User interface:
type User interface {
Clone() User
Name() string
SetName(name string)
}
type Admin struct {
name string
}
func (a *Admin) Clone() User {
u := *a
return &u
}
func (a *Admin) Name() string {
return a.name
}
func (a *Admin) SetName(name string) {
a.name = name
}
func main() {
var user1 User
user1 = &Admin{name:"user1"}
fmt.Printf("User1's name: %s\n", user1.Name())
var user2 User
user2 = user1.Clone()
user2.SetName("user2")
fmt.Printf("User2's name: %s\n", user2.Name())
// output: User2's name: user2
fmt.Printf("User1's name: %s\n", user1.Name())
// output: User1's name: user1
}
Of course in this case, the implementation gets linked to the interface, which might not be desired, but that's a solution.