简体   繁体   中英

How to type assert ad-hoc struct to concrete struct

I have a question about type assertions between a struct generated with reflect and a normal struct.

I know that this is a very limited use cases, but I would still like to know why it won't work.

https://play.golang.org/p/Ko7e8ysgjCk

package main

import (
    "fmt"
    "math/big"
    "reflect"
)

type TupleT struct {
    X *big.Int
    Y *big.Int
}

func main() {
    var fields []reflect.StructField
    fields = append(fields, reflect.StructField{
        Name: "X",
        Type: reflect.TypeOf(new(big.Int)),
        //Tag:  reflect.StructTag("json:\"" + "x" + "\""),
    })
    fields = append(fields, reflect.StructField{
        Name: "Y",
        Type: reflect.TypeOf(new(big.Int)),
        //Tag:  reflect.StructTag("json:\"" + "y" + "\""),
    })
    a := reflect.New(reflect.StructOf(fields)).Elem().Interface()
    if _ = a.(TupleT); true {
        fmt.Println("Hello, playground")
    }

    b := new (struct {X *big.Int "json:\"x\""; Y *big.Int "json:\"y\""})
    interf2 := interface{}(b)
    if _ = interf2.(*TupleT); true {
        fmt.Println("Hello, playground")
    }
}

Because that's not how type assertion works:

 x.(T)

asserts that x is not nil and that the value stored in x is of type T .

A type assertion asserts that the type in the interface value is "exactly" T . In your case that doesn't hold. You have an unnamed struct value in the interface, and it cannot be identical to the named type main.TupleT . It may be that the types have the same underlying types or that they are convertible, but they are not identical, so the type assertion fails.

You may do what you want if you convert the value using Value.Convert() :

a := reflect.New(reflect.StructOf(fields)).Elem().
    Convert(reflect.TypeOf(TupleT{})).Interface()
if _, ok := a.(TupleT); ok {
    fmt.Println("Hello, playground")
}

Similarly, when using type assertion without using reflection, since the interface value holds a value of an unnamed struct type, you have to type assert that:

b := new(struct {
    X *big.Int `json:"x"`
    Y *big.Int `json:"x"`
})
interf2 := interface{}(b)
if _, ok := interf2.(*struct {
    X *big.Int `json:"x"`
    Y *big.Int `json:"x"`
}); ok {
    fmt.Println("Hello, playground")
}

Try the examples on the Go Playground .

Note that in the 2nd case you also have to specify the struct tags even though in the first example it wasn't needed. The reason for this is because structs with identical fields (not considering tags) are convertible to each other, but they are not identical.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM