Страницы

Поиск по вопросам

суббота, 1 февраля 2020 г.

Как красиво передать slice с разными скалярными типами в функцию?

#golang #типы_данных


Есть функция, которая обрезает slice со второго элемента и принимает аргумент с типом
[]interface{}

func SliceShift(slice []interface{}) []interface{} {
    if len(slice) > 1 {
        return slice[1:]
    }
    return make([]interface{}, 0)
}


Если я попытаюсь передать в аргументе срез строк

SliceShift([]string{"a", "b", "c"})


то получаю ошибку: cannot use []string literal (type []string) as type []interface
{} in argument to SliceShift.
Не очень понимаю почему она происходит, но нагулил решение:

str := []string{"a", "b", "c"}
newStr := make([]interface{}, len(str))
for i, v := range str {
    newStr[i] = v
}


Проблема в том, что мне не хочется каждый раз писать make([]interface{}, ...) и цикл
копирования.
Можно ли это как-то избежать?
    


Ответы

Ответ 1



API довольно странное и будет и далее порождать подобные проблемы. Делайте хелпер: type any = interface{} func ss2anys(ss []string) (anys []any) { anys = make([]any, len(ss)) for i := range ss { anys[i] = ss[i] } return anys } Playground: https://play.golang.org/p/B2xyfjUYYQu.

Ответ 2



Из faq по golang Can I convert a []T to an []interface{}? Not directly. It is disallowed by the language specification because the two types do not have the same representation in memory. It is necessary to copy the elements individually to the destination slice. This example converts a slice of int to a slice of interface{}: t := []int{1, 2, 3, 4} s := make([]interface{}, len(t)) for i, v := range t { s[i] = v } Поэтому тут либо функции хелперы под каждый тип, либо так еще можно https://play.golang.org/p/zwqv9ulWW_B package main import ( "fmt" "log" ) func main() { fmt.Printf("String test: %v\n", SliceShift([]string{"a", "b", "c"})) fmt.Printf("String test: %v\n", SliceShift([]string{})) fmt.Printf("Int test: %v\n", SliceShift([]int{1, 2, 3, 4, 5})) names := []string{"Igor", "John", "Garry"} names = SliceShift(names).([]string) fmt.Printf("Names test: %v\n", names) } func SliceShift(slice interface{}) interface{} { switch v := slice.(type) { case []string: if len(v) > 1 { return v[1:] } return make([]string, 0) case []int: if len(v) > 1 { return v[1:] } return make([]int, 0) default: log.Panicf("unknown slice type %#v", v) } return make([]interface{}, 0) }

Ответ 3



Попробуйте так : func SliceShift(slice interface{}) interface{} { s := slice.([]string) return s[1:] } func main() { fmt.Println(SliceShift([]string{"a", "b", "c"})) } вот рабочий пример если в функцию нужно передавать разные типы (можно сделать в SliceShift 2й аргумент который будет указывать тип) и внутри функции проверку к какому типу преобразовать. Вот пример: func SliceShift(slice interface{}, tp int) interface{} { switch (tp) { case 0: return slice.([]string)[1:] case 1: return slice.([]uint64)[1:] case 2: return slice.([]byte)[1:] case 3: return slice.([]float32)[1:] } return struct{}{} } func main() { fmt.Println(SliceShift([]byte("test"), 2)) fmt.Println(SliceShift([]string{"a", "b", "c"}, 0)) } только учтите что передав не корректные типы будет паника.

Комментариев нет:

Отправить комментарий