您将如何访问传递给在Go中期望一个空接口的函数的基础数组?

您将如何访问传递给在Go中期望一个空接口的函数的基础数组?

问题描述:

So let's say that we have a function of the following form:

func WorkMagic(obj interface{}) interface{} {
    switch t := obj.(type) { 
    case string:
        // Do string magic
    default:
        // Do slice magic
    }
    ...
}

I am expecting obj to be either a string or a slice, which I can ascertain via the switch. In the case of a slice, I want to be able to do ordering work on any arbitrary slice, regardless of type. Seems like the best way to accomplish this is using the unsafe package in a similar fashion to that discussed in this article.

Here however, the function accepts a specific type of slice ([]string), whereas I would like to be able to work on any slice. So the question is, given that I am accepting an empty interface as input, how might I access the underlying slice / array using unsafe.Pointer so as to be able to loop through and modify which value is associate with which index?

因此,假设我们具有以下形式的函数: p>

   func WorkMagic(obj interface {})interface {} {
 switch t:= obj。(type){
 case string:
 //做字符串魔术
默认值:
 //做切片魔术 
} 
 ... 
} 
  code>  pre> 
 
 

我希望 obj code>是字符串或切片,我希望 可以通过开关确定。 对于切片,我希望能够对任意切片进行排序工作,而与类型无关。 似乎达到此目的的最佳方法是使用与本文。 p>

但是,此函数接受特定类型的切片( [] string code>),而我 希望能够在任何切片上工作。 所以问题是,假设我接受一个空接口作为输入,我该如何使用unsafe.Pointer访问基础切片/数组,以便能够遍历并修改与哪个索引关联的值? p > div>

You'll want to use reflection. It enables you to work generically without giving up type and memory safety like unsafe would. Read the Go blog's Laws of Reflection.

func actOnSlices(i interface{}) {

    v := reflect.ValueOf(i)

    for v.Kind() == reflect.Ptr { // dereference pointers
        v = v.Elem()
    }

    if v.Kind() != reflect.Slice { // ensure you actually got a slice
        panic("given argument is not a slice")
    }

    // do slice stuff

}

Edit to answer your second question:

Yes – this can be done: elements of a slice are adressable and hence settable. See the following working example:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    s := []string{"foo", "bar"}
    fmt.Println(swapIndexes(s, 0, 1)) // prints [bar foo]
}

func swapIndexes(i interface{}, x, y int) interface{} {

    v := reflect.ValueOf(i)

    for v.Kind() == reflect.Ptr { // dereference pointers
        v = v.Elem()
    }

    if v.Kind() != reflect.Slice { // ensure you actually got a slice
        panic("given argument is not a slice")
    }

    t := v.Index(x).Interface()

    v.Index(x).Set(v.Index(y))

    v.Index(y).Set(reflect.ValueOf(t))

    return v.Interface()

}

Edit to answer your third question:

The unsafe package is not something you'll encounter much in user-land code. It exists to implement certain features (e.g. reflection, C interaction) that need to circumvent Go's safety guarantees to work. Using unsafe is unsafe, as the name suggests, because you can mess up big time without even realizing. By using unsafe, you're incurring in a big trade-off, so it better be worth it. Quoting @twotwotwo:

The downside of unsafe is that if you mess up you're in the old days of segfaults, memory corruption, and buffer-overflow security holes.

Also, as @twotwotwo suggested; it's more "Go-like" to repeat code than using reflection to achieve genericity.

To Go's type-system, []string and []int are two completely separate and unrelated types. just as int and string would be. The relation (both are slices) is obvious only to the programmer. There is no way of expressing "a slice" without saying a slice of what.