Search code examples
gocastingtype-conversiontypecheckinggo-reflect

Determining the type of an interface {} value returned from a function in Golang


I have a function that returns a value from an enum. The enum definition is as follows:

type DataType int64

const (
    INT DataType = iota
    FLOAT
    STRING
    BOOL
    CHAR
    VOID
    ERROR
    BREAK
    CONTINUE
)

    func (l *TSwiftVisitor) VisitWhileInstr(ctx *parser.WhileInstrContext) interface{} {        
    if condExpression.ValType == BOOL {             
        condResult := condExpression.asBool()       
        for condResult {            
            for _, currentInstr := range ctx.InstrList().AllInstr() {
                execResult := l.Visit(currentInstr)
                fmt.Printf("TYPE -> %T\n", execResult) // Prints exec.DataType (the type)
                fmt.Printf("VALUE -> %v\n", execResult) // Prints 5 (the enum value)
                if execResult == BREAK { // This never executes
                    fmt.Println("Es break")
                    return VOID
                } else { // This executes
                    fmt.Println("Es otra mierda")
                }
            }           
            condResult = l.Visit(ctx.Expr()).(*Expression).asBool()
        }       
    } else {
        return ERROR
    }
    return VOID
}

The signature for the Visit method is as follows

Visit(tree antlr.ParseTree) interface{}

After calling the method I receive a value of type DataType, and I'm printing the type and return value in the following lines.

fmt.Printf("TYPE -> %T\n", execResult) // Prints exec.DataType (the type)
fmt.Printf("VALUE -> %v\n", execResult) // Prints 5 (the enum value)

And the output is the following:

TYPE -> exec.DataType                   
VALUE -> 5 

Up to this point everything is fine, however I need to make a comparison and that is where I have a problem and that is that there is something that I am not understanding well about Golang. I have the following:

if execResult == BREAK { // This never executes
    fmt.Println("It's a break")
    return VOID
} else { // This executes
    fmt.Println("It's another thing")
}

And this is where I don't know how to continue with the verification of the return type, if I try the following lines I never execute the code that I want, which in this case is to return VOID. My problem is how to compare the return type to perform a specific action depending on the result. I have also tried the following:

switch execResult.(type) {
    case DataType:
        if execResult.(DataType) == BREAK {

            return execResult
        }
}

In this case, the case within the switch is not fulfilled either. My question is basically how can I determine the type of an interface {} value returned from a function call.


Solution

  • I think @Charlie Tumahai is right: the problem is a value mismatch. I tried a small example on the Go Playground and it works like we expect: if a DataType is returned from Visit then a comparison to a DataType can be true.

    The returned type must be of type DataType. This is demonstrated by the Visit2 method: it returns an int64 which will never be equal to BREAK.

    This is covered in The Go Programming Language Specification under Comparison Operators:

    A value x of non-interface type X and a value t of interface type T can be compared if type X is comparable and X implements T. They are equal if t's dynamic type is identical to X and t's dynamic value is equal to x.

    package main
    
    import "fmt"
    
    type DataType int64
    
    const (
        INT DataType = iota
        BREAK
        CONTINUE
    )
    
    func Visit() interface{} { return BREAK }
    func Visit2() interface{} {return int64(BREAK) }
    
    func main() {
        for _, x := range []interface{}{Visit(), Visit2()} {
            fmt.Printf("x = %v, T(x) = %T : ", x, x)
            if x == BREAK {
                fmt.Println("x is BREAK")
            } else {
                fmt.Println("Cannot identify x")
            }
        }
    
        // Output:
        // x = 1, T(x) = main.DataType : x is BREAK
        // x = 1, T(x) = int64 : Cannot identify x
    }