"impossible" type switch case

959 views
Skip to first unread message

Keith Rarick

unread,
Jan 13, 2012, 3:58:48 PM1/13/12
to golang-nuts
Last week Blake pointed out something surprising to me.

Given

var r io.Reader

one can write

_, _ = r.(io.Writer)

but not

switch r.(type) {
case io.Writer:
}

The type switch results in "impossible type switch
case: r (type io.Reader) cannot have dynamic type
io.Writer (missing Read method)". But I think that
the concrete value r points to can have dynamic type
io.Writer, so I'm a little confused.

What am I missing?

kr

Kyle Lemons

unread,
Jan 13, 2012, 4:12:15 PM1/13/12
to Keith Rarick, golang-nuts
It works for me on the playground...

Brad Fitzpatrick

unread,
Jan 13, 2012, 4:26:08 PM1/13/12
to Kyle Lemons, Keith Rarick, golang-nuts
But not at tip:

$ cat x.go 
package main

import "io"

func main() {
var r io.Reader
_, _ = r.(io.Writer)
switch r.(type) {
case io.Writer:
}
}

$ go run x.go
# _/x
./x.go:9: impossible type switch case: r (type io.Reader) cannot have dynamic type io.Writer (missing Read method)

Jan Mercl

unread,
Jan 13, 2012, 5:00:32 PM1/13/12
to golan...@googlegroups.com
IMO the tip gets it totally right.

Kyle Lemons

unread,
Jan 13, 2012, 5:39:30 PM1/13/12
to golan...@googlegroups.com

I'm not so sure.  For instance, it prevents you from type switching with a case for Hijacker or Flusher or StringWriter when you have one of the more basic writers.  I can't seem to find the change in which this was introduced, which may explain the reason for it.

(sent from gmail mobile)

Jan Mercl

unread,
Jan 13, 2012, 6:04:12 PM1/13/12
to golan...@googlegroups.com
r's (concrete interface) type statically restricts its method set, type switch on r is just pointless. What might work as probably intended is something like"switch x := interface{}(r); x.(type) { ... }"

(Not tested, typing on a phone)

Russ Cox

unread,
Jan 13, 2012, 6:22:53 PM1/13/12
to Keith Rarick, golang-nuts

This is a bug in the compiler. I noticed this a few days
ago, and I'm not sure when it went in. The workaround,
as others have noted, is to write an if statement instead
(or to say case interface { Reader; Writer }:).

Will be fixed soon. Sorry about the trouble.
Russ

Steven Blenkinsop

unread,
Jan 13, 2012, 6:29:33 PM1/13/12
to golan...@googlegroups.com, golan...@googlegroups.com
On 2012-01-13, at 6:04 PM, Jan Mercl <jan....@nic.cz> wrote:

> r's (concrete interface) type statically restricts its method set, type switch on r is just pointless.

I disagree. The interface type specifies a minimum requirement for functionality. The algorithm may be able to use a more specialized interface to run more efficiently, and resort to the original interface type only in the general case. If you have multiple possible special cases you want to handle, switching on a value of non-empty interface type is reasonable.

> What might work as probably intended is something like"switch x := interface{}(r); x.(type) { ... }"

It would have to, or else fmt wouldn't work. The boundary cases should not behave differently from every other case. There is no reason for an empty interface to work differently from every other interface. This must be a bug.

Gustavo Niemeyer

unread,
Jan 13, 2012, 6:46:55 PM1/13/12
to r...@golang.org, Keith Rarick, golang-nuts
> This is a bug in the compiler.  I noticed this a few days
> ago, and I'm not sure when it went in.  The workaround,
> as others have noted, is to write an if statement instead
> (or to say case interface { Reader; Writer }:).

FWIW, went in on the first appearance of the check:

changeset: 10380:e19afb349d0e
user: Luuk van Dijk <l...@golang.org>
date: Wed Nov 09 10:58:53 2011 +0100
summary: gc: Better typechecks and errors in switches.

--
Gustavo Niemeyer
http://niemeyer.net
http://niemeyer.net/plus
http://niemeyer.net/twitter
http://niemeyer.net/blog

-- I'm not absolutely sure of anything.

unread,
Jan 13, 2012, 6:56:29 PM1/13/12
to golang-nuts
On Jan 14, 12:04 am, Jan Mercl <jan.me...@nic.cz> wrote:
> r's (concrete interface) type statically restricts its method set, type switch on r is just pointless.

This sentence contradicts the language specification.

Jan Mercl

unread,
Jan 14, 2012, 5:13:05 AM1/14/12
to golan...@googlegroups.com
On Saturday, January 14, 2012 12:22:53 AM UTC+1, Russ Cox wrote:

This is a bug in the compiler.  I noticed this a few days
ago, and I'm not sure when it went in.  The workaround,
as others have noted, is to write an if statement instead
(or to say case interface { Reader; Writer }:).

Consulting the specs - you're right. Every interface type has a dynamic type, not only type interface{}. I nonsensically assumed only the later, probably as it is most seen. Sorry for the noise.
 
On Saturday, January 14, 2012 12:29:33 AM UTC+1, Steven Blenkinsop wrote:

I disagree.

Rightfully. See above.
 
On Saturday, January 14, 2012 12:56:29 AM UTC+1, ⚛ wrote:
On Jan 14, 12:04 am, Jan Mercl <jan....@nic.cz> wrote: 
> r's (concrete interface) type statically restricts its method set, type switch on r is just pointless. 

This sentence contradicts the language specification.

Yes and no. The wrong part is about the type switch only (cf. reply to Russ above).

package main

import "io"

func main() {
        var rw io.ReadWriter
        var r io.Reader
        r = rw // OK
        r.Write([]byte{}) // prog.go:9: r.Write undefined (type io.Reader has no field or method Write)
}

Only io.Reader methods (the static method set) are available for r even though its dynamic type can have a superset of io.Reader methods. Anyway, that's what static typing is for ;-)

Type switch (don't to be confused with type assertion) on a static type is pointless, except for interface types. That's where I errored.

unread,
Jan 14, 2012, 6:57:22 AM1/14/12
to golang-nuts
On Jan 14, 11:13 am, Jan Mercl <jan.me...@nic.cz> wrote:
> On Saturday, January 14, 2012 12:56:29 AM UTC+1, ⚛ wrote:
>
> > On Jan 14, 12:04 am, Jan Mercl <jan....@nic.cz> wrote:
> > > r's (concrete interface) type statically restricts its method set, type
> > switch on r is just pointless.
>
> > This sentence contradicts the language specification.
>
> Yes and no. The wrong part is about the type switch only (cf. reply to Russ
> above).

About the 1st part of your sentence:

There is no such thing as "restricts its method set" in Go. There is
"partial information about the full method set of a particular non-
interface type".

Jan Mercl

unread,
Jan 14, 2012, 7:23:54 AM1/14/12
to golan...@googlegroups.com
On Saturday, January 14, 2012 12:57:22 PM UTC+1, ⚛ wrote:
There is no such thing as "restricts its method set" in Go. There is
"partial information about the full method set of a particular non-
interface type".

Given:

var r io.Reader

the method set which can be used via r (like r.Foo()) is limited to the method set of io.Reader *regardless* of the concrete value and its type stored in r - which dynamic type can have a superset of io.Reader method set implemented, hence the "restriction". I don't think there's any contradiction to Go specs in this as this is actually enforced by the compiler even though the term "restirction" is not used in the specs when talking about this mechanism.

In this quote from Rob Pike's "The Laws of Reflection"...

[Q]
A variable of interface type stores a pair: the concrete value assigned to the variable, and that value’s type descriptor. To be more precise, the value is the underlying concrete data item that implements the interface and the type describes the full type of that item. For instance, after

var r io.Reader
tty, err = os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil { return nil, err }
r = tty

r contains, schematically, the (value, type) pair, (tty*os.File). Notice that the type *os.Fileimplements methods other than Read; even though the interface value provides access only to the Read method, the value inside carries all the type information about that value. That's why we can do things like this:

var w io.Writer
w = r.(io.Writer)

The expression in this assignment is a type assertion; what it asserts is that the item inside ralso implements io.Writer, and so we can assign it to w. After the assignment, w will contain the pair (tty*os.File). That's the same pair as was held in r. The static type of the interface determines what methods may be invoked with an interface variable, even though the concrete value inside may have a larger set of methods.
[/Q]

... the " the interface value provides access *only* to the Read method ..." and "The *static* type of the interface determines ..." parts talks about the same thing I called "restriction" or "statically restricted" - as in contrast to "dynamic restriction" (must satisfy the method set) like seen in the type assertion with w.
Reply all
Reply to author
Forward
0 new messages