When an interface (empty or otherwise) is assigned to a variable, it carries type information that can be queried at runtime. Type assertion is a mechanism that is available in Go to idiomatically narrow a variable (of interface
type) down to a concrete type and value that are stored in the variable. The following example uses type assertion in the eat
function to select which food
type to select in the eat
function:
type food interface { eat() } type veggie string func (v veggie) eat() { fmt.Println("Eating", v) } type meat string func (m meat) eat() { fmt.Println("Eating tasty", m) } func eat(f food) { veg, ok := f.(veggie) if ok { if veg == "okra" { fmt.Println("Yuk! not eating ", veg) }else{ veg.eat() } return } mt, ok := f.(meat) if ok { if mt == "beef" { fmt.Println("Yuk! not eating ", mt) }else{ mt.eat() } return } fmt.Println("Not eating whatever that is: ", f) }
golang.fyi/interface_assert.go
The eat
function takes the food
interface type as its parameter. The code shows how to use idiomatic Go to extract the static type and value stored in the f
interface parameter using assertion. The general form for type assertion expression is given as follows:
<interface_variable>.(concrete type name)
The expression starts with the variable of the interface type. It is then followed by a dot and the concrete type being asserted enclosed in parentheses. The type assertion expression can return two values: one is the concrete value (extracted from the interface) and the second is a Boolean indicating the success of the assertion, as shown here:
value, boolean := <interface_variable>.(concrete type name)
This is the form of assertion that is shown in the following snippet (extracted from the earlier example) when narrowing the f
parameter to a specific type of food
. If the type is asserted to be meat
, then the code continues to test the value of the mt
variable:
mt, ok := f.(meat) if ok { if mt == "beef" { fmt.Println("Yuk! not eating ", mt) }else{ mt.eat() } return }
A type assertion expression can also return just the value, as follows:
value := <interface_variable>.(concrete type name)
This form of assertion is risky to do as the runtime will cause a panic in the program if the value stored in the interface variable is not of the asserted type. Use this form only if you have other safeguards to either prevent or gracefully handle a panic.
Lastly, when your code requires multiple assertions to test many types at runtime, a much nicer idiom for assertions is the type switch
statement. It uses the switch
statement semantic to query static type information from an interface value using case clauses. The eat
function from the previous food-related example can been updated to use a type switch
instead of if
statement, as shown in the following code snippet:
func eat(f food) { swtich morsel := f.(type){ case veggie: if morsel == "okra" { fmt.Println("Yuk! not eating ", mosel) }else{ mosel.eat() } case meat: if morsel == "beef" { fmt.Println("Yuk! not eating ", mosel) }else{ mosel.eat() } default: fmt.Println("Not eating whatever that is: ", f) } }
golang.fyi/interface_assert2.go
Notice the code is much nicer to read. It can support any number of cases and is clearly laid out with visual clues that makes it easy to reason about. The switch
type also makes the panic issue go away by simply specifying a default case that can handle any types not specifically handled in the case clause.
3.14.252.56