I have a function that can take a number of different argument types. I'd like to use a type switch and reduce code duplication as much as possible. As a very basic example, here I want to copy both uint8
and int8
types into a byte buffer. This code happily works
package main
func switchFn(args ...interface{}) {
var buf []byte
for _, arg := range args {
switch val := arg.(type) {
case uint8:
buf = append(buf, byte(val))
case int8:
buf = append(buf, byte(val))
}
}
}
func main() {
switchFn(int8(42), uint8(42)) // etc
}
You'll notice both the case statements do exactly the same thing! If I combine them though...
package main
func switchFn(args ...interface{}) {
var buf []byte
for _, arg := range args {
switch val := arg.(type) {
case uint8, int8:
buf = append(buf, byte(val))
}
}
}
func main() {
switchFn(int8(42), uint8(42)) // etc
}
I run into an issue of cannot convert val (type interface {}) to type byte: need type assertion
. But I'm literally switching on the type! Argh!
Am I stuck with the code duplication here, or is there a smarter way to do this? Note that the copying into byte buffer is used to illustrate the example, my function may be doing other things in the case blocks.
CodePudding user response:
Here's a way to avoid the code duplication, at the cost of ... well, a different sort of code duplication:
func switchFn(args ...interface{}) {
var buf []byte
for _, arg := range args {
var val byte
switch v := arg.(type) {
case uint8:
val = byte(v)
case int8:
val = byte(v)
default:
panic("wrong type")
}
buf = append(buf, val)
}
}
For this particular function, the original duplication is probably better. Should the buf = append(buf, val)
section get larger or more complicated, this would probably be better.
In still other cases—perhaps most real ones—the method gopher suggests is probably best:
f := func(val byte) {
buffer = append(buffer, val)
}
You can now call f
from each case
.
CodePudding user response:
The cases can be combined, but val
will have type interface{}
in the block. That's not useful for your scenario.
Use a function to reduce code duplication.
func switchFn(args ...interface{}) {
var buf []byte
byteFn := func(b byte) {
buf = append(buf, b)
}
for _, arg := range args {
switch val := arg.(type) {
case uint8:
byteFn(val)
case int8:
byteFn(byte(val))
}
}
}
The reflect
API will not help because separate code is required for the signed and unsigned values. The reflect
API is helpful for combining all signed integers into block of code and all unsigned integers into another block of code.
for _, arg := range args {
switch val := arg.(type) {
case int, int8, int16, int32, int64:
i := reflect.ValueOf(val).Int()
// i is an int64
fmt.Println(i)
case uint, uint8, uint16, uint32, uint64:
u := reflect.ValueOf(val).Uint()
// u is an uint64
fmt.Println(u)
}
}