dongxing7318 2013-10-10 16:54
浏览 70
已采纳

Golang:交换两个数字的接口

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. How do I use interface and swap two input numbers? Do I need to define two structures?

type num struct {
    value interface{}
}

type numbers struct {
    b *num
    c *num
}

func (a *num) SwapNum(var1, var2 interface{}) {
    var a num

    temp := var1
    var1 = var2
    var2 = temp
}

func main() {
    a := 1
    b := 2
    c := 3.5
    d := 5.5

    SwapNum(a, b)
    fmt.Println(a, b) // 2 1
    SwapNum(c, d)
    fmt.Println(c, d) // 5.5 3.5
}
  • 写回答

1条回答 默认 最新

  • dongshanyan0322 2013-10-10 17:58
    关注

    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).

    Can we do better?

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 slaris 系统断电后,重新开机后一直自动重启
  • ¥15 QTableWidget重绘程序崩溃
  • ¥15 51寻迹小车定点寻迹
  • ¥15 谁能帮我看看这拒稿理由啥意思啊阿啊
  • ¥15 关于vue2中methods使用call修改this指向的问题
  • ¥15 idea自动补全键位冲突
  • ¥15 请教一下写代码,代码好难
  • ¥15 iis10中如何阻止别人网站重定向到我的网站
  • ¥15 滑块验证码移动速度不一致问题
  • ¥15 Utunbu中vscode下cern root工作台中写的程序root的头文件无法包含