Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does the keyword type mean when used in a switch?

Tags:

go

I have seen several instances of this code in golang:

func process(node ast.Node) Foo {
    switch n := node.(type) {
        // ... removed for brevity
    }
}

ast.Node is an interface. Is the node.(type) snippet code to perform reflection; to find out the actual implementers of the interface ?

like image 759
canadadry Avatar asked Feb 26 '14 08:02

canadadry


1 Answers

Yes. It's called a Type switch. It allows you to execute code depending on the actual type of the interface you pass.

I think the official documentation, with its example, is clear :

A switch can also be used to discover the dynamic type of an interface variable. Such a type switch uses the syntax of a type assertion with the keyword type inside the parentheses. If the switch declares a variable in the expression, the variable will have the corresponding type in each clause. It's also idiomatic to reuse the name in such cases, in effect declaring a new variable with the same name but a different type in each case.

var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
    fmt.Printf("unexpected type %T", t)       // %T prints whatever type t has
case bool:
    fmt.Printf("boolean %t\n", t)             // t has type bool
case int:
    fmt.Printf("integer %d\n", t)             // t has type int
case *bool:
    fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
case *int:
    fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}

You should not use that too often in a properly typed program but it's convenient when you need it. An example of use : Suppose you implement a database driver, you may have to do conversions depending on the type of the Go variables. Here's an extract of the go-sql/mysql driver :

// Scan implements the Scanner interface.
// The value type must be time.Time or string / []byte (formatted time-string),
// otherwise Scan fails.
func (nt *NullTime) Scan(value interface{}) (err error) {
    if value == nil {
        nt.Time, nt.Valid = time.Time{}, false
        return
    }

    switch v := value.(type) {
    case time.Time:
        nt.Time, nt.Valid = v, true
        return
    case []byte:
        nt.Time, err = parseDateTime(string(v), time.UTC)
        nt.Valid = (err == nil)
        return
    case string:
        nt.Time, err = parseDateTime(v, time.UTC)
        nt.Valid = (err == nil)
        return
    }

    nt.Valid = false
    return fmt.Errorf("Can't convert %T to time.Time", value)
}
like image 171
Denys Séguret Avatar answered Nov 18 '22 18:11

Denys Séguret