-1

In the following code

    var r io.Reader
    c, _ := net.Dial("tcp", ":8080")
    r = c 
    switch r.(type) {
    case io.Reader:
        fmt.Println("r implements the reader interface")
        // fallthrough
    case io.Writer:
        fmt.Println("r implements the writer interface")
    case net.Conn:
        fmt.Println("r implements the conn interface")
    }

the first case statement is the one that's always executed.

If I uncomment fallthrough the code does not compile given that fallthrough is not allowed in a type switch

If I am not wrong a go interface has 2 values:

  • the actual TYPE (descriptor) stored in the interface (e.g. io.Writer, io.Reader etc)
  • the value stored in it

Since r apparently satisfies all of the above case statements, what is the way of finding the exact type stored in r?

Is it only through reflection that this cone be done?

icza
  • 389,944
  • 63
  • 907
  • 827
pkaramol
  • 16,451
  • 43
  • 149
  • 324
  • 2
    I'm confused. You've defined `r` as a value of type `io.Reader` -- why would you expect that its type match anything but `io.Reader`? – Adam Smith Nov 01 '20 at 18:37
  • I am trying to get some more insights about the type switch functionality; and apparently `r` does match all of the above – pkaramol Nov 01 '20 at 18:39
  • Does this answer your question? https://stackoverflow.com/questions/32113597/checking-of-variable-implements-interface-without-compiling –  Nov 02 '20 at 01:18

2 Answers2

4

The type switch as well as the type assertion may be used to check if an interface value holds a value of a concrete type (and at the same time extract a value of that concrete type). If the type you check (or type assert) is an interface type (and not a concrete type), the case will match (or the assertion will hold) if the concrete type implements the given interface type.

net.Conn is an interface that is a superset of both io.Reader and io.Writer. If something implements net.Conn, it automatically implements io.Reader and io.Writer as well, so if you check io.Reader first, that will match (if the type also implements net.Conn).

If you want to use a type switch to check for implemented interfaces, check for net.Conn first, and if it's not implemented, list io.Reader and io.Writer later on.

switch r.(type) {
case net.Conn:
    fmt.Println("r implements the conn interface")
case io.Reader:
    fmt.Println("r implements the reader interface")
case io.Writer:
    fmt.Println("r implements the writer interface")
}

If I am not wrong a go interface has 2 values:

  • the actual TYPE (descriptor) stored in the interface (e.g. io.Writer, io.Reader etc)
  • the value stored in it

You are not wrong but you miss a key point: the actual concrete type. io.Conn and io.Reader are not concrete types, they are inteface types.

An example concrete type that may be returned by net.Dial() is *net.TCPConn.

icza
  • 389,944
  • 63
  • 907
  • 827
1

Use this code to find the concrete type of the value:

var r io.Reader
c, _ := net.Dial("tcp", ":8080")
r = c 
fmt.Printf("The type of r is %T\n", r)

The reflect package can also be used to find the concrete type:

t := reflect.TypeOf(r)
fmt.Println("The type of or is", t)

Use separate if statements to check support for each interface. A type switch cannot test for all interfaces because the switch continues on the the first type to match.

if _, ok := r.(io.Reader); ok {
    // we know this already because r is an io.Reader
    fmt.Println("r implements the reader interface")
}
if _, ok := r.(io.Writer); ok {
    fmt.Println("r implements the writer interface")
}
if _. ok := r.(net.Conn); ok {
    fmt.Println("r implements the conn interface")
}

We know that the conditions in all of the if statements evaluate to true because the value in r satisfies the net.Conn interface.