Assume we want to create a union type of a node that may be a text node or a container node that holds text nodes. The following code (playground) demonstrates this:
package main type node interface { node() } type textNode struct { text string } func (textNode) node() {} type containerNode []node func (containerNode) node() {} func main() { nodes := []node{ textNode{"hello"}, textNode{" "}, containerNode{ textNode{"world"}, textNode{"!"}, }, } printNodes(nodes) } func printNodes(nodes []node) { for _, n := range nodes { switch n := n.(type) { case textNode: fmt.Print(n.text) case containerNode: printNodes([]node(n)) } } }
This trick does in fact give us some type checking during compile time: the following code
switch node := node.(type) { case string: }
will error out when built:
impossible type switch case: n (type node) cannot have dynamic type string (missing node method)