I want to swap two numbers using interface but the interface concept is so confusing to me.
http://play.golang.org/p/qhwyxMRj-c
This is the code and playground.
First of all, the interface{}
type is simply a type which accepts all values as it is an interface with an empty method set and every type can satisfy that. int
for example does not have any methods, neither does interface{}
.
For a method which swaps the values of two variables you first need to make sure these variables are actually modifiable. Values passed to a function are always copied (except reference types like slices and maps but that is not our concern at the moment). You can achieve modifiable parameter by using a pointer to the variable.
So with that knowledge you can go on and define SwapNum
like this:
func SwapNum(a interface{}, b interface{})
Now SwapNum
is a function that accepts two parameters of any type.
You can't write
func SwapNum(a *interface{}, b *interface{})
as this would only accept parameters of type *interface{}
and not just any type.
(Try it for yourself here).
So we have a signature, the only thing left is swapping the values.
func SwapNum(a interface{}, b interface{}) {
*a, *b = *b, *a
}
No, this will not work that way. By using interface{}
we must do runtime type assertions to check whether we're doing the right thing or not. So the code must be expanded using the reflect
package. This article might get you started if you don't know about reflection.
Basically we will need this function:
func SwapNum(a interface{}, b interface{}) {
ra := reflect.ValueOf(a).Elem()
rb := reflect.ValueOf(b).Elem()
tmp := ra.Interface()
ra.Set(rb)
rb.Set(reflect.ValueOf(tmp))
}
This code makes a reflection of a
and b
using reflect.ValueOf() so that we can
inspect it. In the same line we're assuming that we've got pointer values and dereference
them by calling .Elem() on them.
This basically translates to ra := *a
and rb := *b
.
After that, we're making a copy of *a
by requesting the value using .Interface()
and assigning it (effectively making a copy).
Finally, we set the value of a
to b
with [ra.Set(rb)
]5, which translates to *a = *b
and then assigning b
to a
, which we stored in the temp. variable before. For this,
we need to convert tmp
back to a reflection of itself so that rb.Set()
can be used
(it takes a reflect.Value
as parameter).
Yes! We can make the code more type safe, or better, make the definition of Swap
type safe
by using reflect.MakeFunc. In the doc (follow the link) is an example which is very
like what you're trying. Essentially you can fill a function prototype with content
by using reflection. As you supplied the prototype (the signature) of the function the
compiler can check the types, which it can't when the value is reduced to interface{}
.
Example usage:
var intSwap func(*int, *int)
a,b := 1, 0
makeSwap(&intSwap)
intSwap(&a, &b)
// a is now 0, b is now 1
The code behind this:
swap := func(in []reflect.Value) []reflect.Value {
ra := in[0].Elem()
rb := in[1].Elem()
tmp := ra.Interface()
ra.Set(rb)
rb.Set(reflect.ValueOf(tmp))
return nil
}
makeSwap := func(fptr interface{}) {
fn := reflect.ValueOf(fptr).Elem()
v := reflect.MakeFunc(fn.Type(), swap)
fn.Set(v)
}
The code of swap
is basically the same as that of SwapNum
. makeSwap
is the same
as the one used in the docs where it is explained pretty well.
Disclaimer: The code above makes a lot of assumptions about what is given and
what the values look like. Normally you need to check, for example, that the given
values to SwapNum
actually are pointer values and so forth. I left that out for
reasons of clarity.