lundi 4 janvier 2021

Determine whether instance of arbitrary type can be set to arbitrarily typed value

Is it possible to determine (using reflect) whether an instance of an arbitrary type can be set to an arbitrary value, i.e. to determine whether Value.Set() would panic due to type incompatibility?

An MCVE is set out below. What I want to know is effectively 'can I write set() without using the defer/recover construct?'

I'd like to avoid defer not only because it seems ugly, but because Value.Set() may panic for other reasons.

Note this is not merely a question of comparing the types for equality, as the o2 example below shows.

package main

import (
    "fmt"
    "reflect"
)

// set a value V to interface i, returning true if this can be done, else false
//
// CAN WE WRITE THIS WITHOUT HAVING TO USE DEFER / RECOVER?
//
func set(v reflect.Value, i interface{}) bool {
    success := true
    defer func() {
        if r := recover(); r != nil {
            success = false
        }
    }()
    v.Set(reflect.ValueOf(i))
    return success
}

// get the type of a typed nil
func getType(typedNil interface{}) reflect.Type {
    return reflect.TypeOf(typedNil).Elem()
}

func main() {
    t1 := getType((*int)(nil))
    o1 := reflect.New(t1)

    t2 := getType((*interface{})(nil))
    o2 := reflect.New(t2)

    var ok bool
    var aInt = 456
    var aString string = "hello"
    var aUint uint = 123

    // Set o1 to various types
    
    ok = set(o1.Elem(), aInt) // Should return true
    fmt.Printf("After o1 set to aInt returned %v: obj is type %T content '%v'\n", ok, o1.Elem().Interface(), o1.Elem().Interface())

    ok = set(o1.Elem(), aString) // Should return false
    fmt.Printf("After o1 set to aString returned %v: obj is type %T content '%v'\n", ok, o1.Elem().Interface(), o1.Elem().Interface())

    ok = set(o1.Elem(), aUint) // Should return false
    fmt.Printf("After o1 set to aUint returned %v: obj is type %T content '%v'\n", ok, o1.Elem().Interface(), o1.Elem().Interface())

    fmt.Println("")

    // Set o2 to various types
    ok = set(o2.Elem(), aInt)  // Should return true
    fmt.Printf("After o2 set to aInt returned %v: obj is type %T content '%v'\n", ok, o2.Elem().Interface(), o2.Elem().Interface())

    ok = set(o2.Elem(), aString) // Should return true
    fmt.Printf("After o2 set to aString returned %v: obj is type %T content '%v'\n", ok, o2.Elem().Interface(), o2.Elem().Interface())
    
    ok = set(o2.Elem(), aUint) // Should return true
    fmt.Printf("After o2 set to aUint returned %v: obj is type %T content '%v'\n", ok, o2.Elem().Interface(), o2.Elem().Interface())
}





Aucun commentaire:

Enregistrer un commentaire