Type assertion

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.252.56