Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type switch with multiple cases loses concrete type #41740

Closed
sethvargo opened this issue Oct 1, 2020 · 2 comments
Closed

Type switch with multiple cases loses concrete type #41740

sethvargo opened this issue Oct 1, 2020 · 2 comments

Comments

@sethvargo
Copy link
Contributor

What version of Go are you using (go version)?

go version go1.15.2 darwin/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env

GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/sethvargo/Library/Caches/go-build"
GOENV="/Users/sethvargo/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/sethvargo/Development/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/sethvargo/Development/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/Users/sethvargo/.homebrew/Cellar/go/1.15.2/libexec"
GOSUMDB="sum.golang.org"
GOTMPDIR=""

What did you do?

https://play.golang.org/p/RSGBp79HzqT

package main

func main() {
	v := interface{}(1)

	switch t := v.(type) {
	case int, int64:
		other(int(t))
	}
}

func other(i int) {}

What did you expect to see?

Since all types in the case statement are int, I would expect t to be either an int or int64 inside of the case statement.

What did you see instead?

When given multiple items on a case statement, t is interface{} instead of the concrete type.

More

I think I understand why this behavior exists. For example, given:

switch t := v.(type) {
case int, string:
}

t isn't specific enough.

I would expect either one of the following:

  • This is a compile-time error. Attempting to switch on a type assertion where a case statement has more than one value and the concrete type is used inside the case does not compile.

  • If all the types are of the same type family, allow it.

Without this, you're left with some rather unnecessarily and possibly error-prone Go code like:

switch t := raw.(type) {
case uint:
  id = t
case uint8:
  id = uint(t)
case uint16:
  id = uint(t)
case uint32:
  id = uint(t)
case uint64:
  id = uint(t)
case int:
  id = uint(t)
case int8:
  id = uint(t)
case int16:
  id = uint(t)
case int32:
  id = uint(t)
case int64:
  id = uint(t)
default:
  return fmt.Errorf("bad type %T", t)
}

versus the much shorter and possibly less error-prone:

switch t := raw.(type) {
case uint, uint8, uint16, uint32, uint64, uintptr:
  id = uint64(t)
case int, int8, int16, int32, int64:
  id = uint64(t)
default:
  return fmt.Errorf("bad type %T", t)
}
@sethvargo
Copy link
Contributor Author

/cc @jeremyfaller

@randall77
Copy link
Contributor

This is intentional and written in the language spec.

The TypeSwitchGuard may include a short variable declaration. When that form is used, the variable is declared at the end of the TypeSwitchCase in the implicit block of each clause. In clauses with a case listing exactly one type, the variable has that type; otherwise, the variable has the type of the expression in the TypeSwitchGuard.

Go doesn't really have a way of unioning two types, so that's what we have to do.

Closing. Any change here would have to go through the proposal process.

@golang golang locked and limited conversation to collaborators Oct 1, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants