Reputation: 61
The answer to this is probably pretty obvious, but I'm asking anyways because I failed to find a good explanation for this.
I have two examples that I've made, they do pretty much the same thing, however, the first one uses int's and the other one is using interfaces:
Interface: https://play.golang.org/p/yb2oVaOJGF
type Apple interface{}
type Orange interface{}
type Basket struct {
Fruit interface{}
}
func getFruites(basket Basket) {
switch t := basket.Fruit.(type) {
case Apple:
apples, ok := t.(int)
if !ok {
log.Panic("Rotten apples!")
}
fmt.Println("Apples:", apples)
case Orange:
oranges, ok := t.(int)
if !ok {
log.Panic("Rotten oranges!")
}
fmt.Println("Oranges:", oranges)
}
}
func main() {
appleBasket := Basket{
Fruit: Apple(10),
}
getFruites(appleBasket)
orangeBasket := Basket{
Fruit: Orange(10),
}
getFruites(orangeBasket)
}
Int: https://play.golang.org/p/_z8Mm0II41
type Apple int
type Orange int
type Basket struct {
Fruit interface{}
}
func getFruites(basket Basket) {
switch t := basket.Fruit.(type) {
case Apple:
apples := t
fmt.Println("Apples:", apples)
case Orange:
oranges := t
fmt.Println("Oranges:", oranges)
}
}
func main() {
appleBasket := Basket{
Fruit: Apple(10),
}
getFruites(appleBasket)
orangeBasket := Basket{
Fruit: Orange(10),
}
getFruites(orangeBasket)
}
Could someone please explain why they produce different output?
Upvotes: 2
Views: 2933
Reputation: 491
Since you said other answers didn't feel intuitive, I am attempting to explain it from a completely different standpoint. The case you mention has nothing to do with your type aliases and everything to do with how interfaces work in Go. To more clearly show the difference between the two codes you provided, here is a different code that gives the similar output as your interface example, but doesn't use type aliases at all.
Playground link: https://play.golang.org/p/0tyDV28cp2b
package main
import (
"fmt"
"log"
)
// Create two interfaces that have exactly the same signature
// Why? So every struct that implements one automatically implements the other
type interface1 interface {
Foo() int
}
type interface2 interface {
Foo() int
}
// Create an impl for the interfaces defined above
type impl int
func (i impl) Foo() int { return int(i) }
// Note that the Fruit is of type "interface2"
// Despite that, it will always go to "interface1" case in the type switch
// It is because anything that implements interface2
// also implements interface1
type Basket struct {
Fruit interface2
}
func getFruites(basket Basket) {
switch t := basket.Fruit.(type) {
case interface1:
ans, ok := t.(impl)
if !ok {
log.Panic("Rotten ans!")
}
fmt.Println("Interface1:", ans)
case interface2:
ans, ok := t.(impl)
if !ok {
log.Panic("Rotten ans!")
}
fmt.Println("Interface2:", ans)
}
}
func main() {
appleBasket := Basket{
Fruit: impl(10),
}
getFruites(appleBasket)
orangeBasket := Basket{
Fruit: impl(10),
}
getFruites(orangeBasket)
}
Upvotes: 0
Reputation: 482
Because you are defining the Apple and Orange types to an empty interface they are satisfied by anything.
The empty interface is in essence nothing specific.
Everything in the first example can be asserted as the int type neither Apple or Oranges.
Look at this small change to your first example's code.
package main
import (
"fmt"
"log"
)
type Apple interface{}
type Orange interface{}
type Basket struct {
Fruit interface{}
}
func getFruites(basket Basket) {
switch t := basket.Fruit.(type) {
case int:
fmt.Println("empty interfaces are satisfying an int")
case Apple:
apples, ok := t.(int)
if !ok {
log.Panic("Rotten apples!")
}
fmt.Println("Apples:", apples)
case Orange:
oranges, ok := t.(int)
if !ok {
log.Panic("Rotten oranges!")
}
fmt.Println("Oranges:", oranges)
}
}
func main() {
appleBasket := Basket{
Fruit: Apple(10),
}
getFruites(appleBasket)
orangeBasket := Basket{
Fruit: Orange(10),
}
getFruites(orangeBasket)
}
https://play.golang.org/p/LDtvbXnjT7
Upvotes: 1
Reputation: 23098
In the case where Apple and Orange are defined as interface{}
, the type switch is satisfied by anything that implements that interface.
For the empty interface, that is anything at all, and it takes the first case that matches.
When Apple and Orange are "aliases" for a non-interface type (int), only a variable that is explicitly an Apple or Orange can satisfy the type switch.
Upvotes: 1