Как проверить, является ли интерфейс указателем на фрагмент

# #go #pointers #reflection #slice

Вопрос:

Я знаю, как проверить, является ли интерфейс указателем:

 func isPointerArray(i interface{}) bool {
    if i == nil {
        return false
    }
    if reflect.ValueOf(i).Kind() != reflect.Ptr {
        return false
    }
}
 

Но как я могу проверить, является ли этот указатель срезом? TypeOf является указателем.

Ответ №1:

Если вид значения reflect.Ptr равен, вы можете использовать Value.Elem() для получения указанного значения, и вы можете сравнить его вид с reflect.Slice .

Вот как это будет выглядеть:

 func isPtrSlice(i interface{}) bool {
    if i == nil {
        return false
    }
    v := reflect.ValueOf(i)
    if v.Kind() != reflect.Ptr {
        return false
    }
    return v.Elem().Kind() == reflect.Slice
}
 

Но есть одна загвоздка. Если переданное значение является указателем на тип среза, но само значение является nil указателем, об этом будет сообщено false . Понятно, поскольку нет указанного значения, тип которого мог бы быть срезом. Это может быть или не быть тем, что вы хотите.

Если вы хотите true даже для nil значений указателя (которые относятся к типам указателей на срезы), вы можете использовать reflect.Type вместо reflect.Value . Это работает и для nil указателей, потому что, несмотря на отсутствие указанного значения, все еще существует указанный тип (называемый базовым типом).

 func isPtrSlice2(i interface{}) bool {
    if i == nil {
        return false
    }
    t := reflect.TypeOf(i)
    if t.Kind() != reflect.Ptr {
        return false
    }
    return t.Elem().Kind() == reflect.Slice
}
 

Тестирование вышеуказанных функций:

 vals := []interface{}{
    nil,
    "a",
    amp;image.Point{},
    []string{},
    amp;[]string{},
    (*[]string)(nil),
}

for _, v := range vals {
    fmt.Printf("%-14T isPtrSlice: %-5t, isPtrSlice2: %tn",
        v, isPtrSlice(v), isPtrSlice2(v))
}
 

Который будет выводиться (попробуйте это на игровой площадке Go):

 <nil>          isPtrSlice: false, isPtrSlice2: false
string         isPtrSlice: false, isPtrSlice2: false
*image.Point   isPtrSlice: false, isPtrSlice2: false
[]string       isPtrSlice: false, isPtrSlice2: false
*[]string      isPtrSlice: true , isPtrSlice2: true
*[]string      isPtrSlice: false, isPtrSlice2: true
 

Как вы можете видеть в последней строке (где значение является nil указателем типа *[]string ) isPtrSlice() , возвращает false while isPtrSlice2() returns true .

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

1. для массивов можно использовать reflect.Array , например elem.Kind() == reflect.Slice || elem.Kind() == reflect.Array

2. какой-то качественный ответ icza прямо там. Спасибо

Ответ №2:

Вы можете использовать утверждения типа:

 package main

import "fmt"

func main() {
    var value interface{}
    value = amp;[]int{1, 2}

    if res, ok := value.(*[]int); ok {
        fmt.Println(res)
    }
}
 

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

1. Одним из ограничений является то, что я не знаю тип.