Reputation: 55623
type MyNumber interface {
float32, float64, uint, int // this is not supported
}
func PrintNumber(n MyNumber) {
switch n.(type) {
case float32, float64, uint, int:
fmt.Printf("%v\n", n)
default:
panic("PrintNumber only supports types float32, float64, uint, int")
}
}
In go, you can define a blank interface, which basically allows any type
var v interface{}
v = "string"
v = 0.1
Is there a way to reduce the allowed types to a specific list of types?
Something like
type MyNumber float32, float64, uint, int
or
type MyNumber interface {
float32, float64, uint, int
}
This is so that I can have the compiler check if the type is going to be supported by the function.
Upvotes: 0
Views: 1656
Reputation: 44697
Is there a way to reduce the allowed types to a specific list of types?
Yes, starting with Go 1.18, using interfaces with type elements. But such interfaces can only be used as constraints for type parameters. In the draft version of the specs this is mentioned under Interface types:
Interfaces that contain non-interface types, terms of the form ~T, or unions may only be used as type constraints, or as elements of other interfaces used as constraints. They cannot be the types of values or variables, or components of other, non-interface types.
In short, within an interface, beside method signatures and other embedded interfaces, you can now add type elements. This defines a type constraint, i.e. "the set of permissible type arguments for the respective type parameter".
In particular:
type MyNumber interface {
float32 | float64 | uint | int
}
~
to specify approximation elements, i.e. the set of all types whose underlying type is T
(more info: What's the meaning of the new token ~ in Go (approximation elements)?). These can also be part of unions.type MyNumber interface {
~float32 | ~float64 | ~uint | ~int
}
// set of types with underlying int that also implement `String() string`
type MyNumber interface {
~int
String() string
}
// empty type set
type MyNumber interface {
int
String() string
}
As mentioned earlier, you use this only as a type parameter constraint, you won't be able to use it in type switches, declare variables, etc.:
func foo[T MyNumber](v T) {
// ...
}
Upvotes: 1
Reputation: 417612
You can't force to a set of concrete types at compile time in a way you'd like to.
What you can do is use interfaces. List the methods in the interface you expect from the implementations. Then it won't matter what the concrete, runtime type is. You interact with the value only via the interface, which is guaranteed at compile time that it is implemented by the passed value.
E.g. if you expect the value to provide an int32
value, use this interface:
type HasInt32 interface {
Int32() int32
}
func f(i HasInt32) {
fmt.Println("int32 value:", i.Int32())
}
It won't matter if the passed value has int32
or float64
as its underlying type, or it's whatever else complex type. You need an int32
, and that's what you get. The implementors are responsible how to produce that value.
For example:
type MyInt32 int32
func (m MyInt32) Int32() int32 { return int32(m) }
type MyStruct struct {
i int64
}
func (m MyStruct) Int32() int32 { return int32(m.i) }
Testing it:
var m MyInt32 = 1
f(m)
var s MyStruct = MyStruct{i: 2}
f(s)
Which outputs (try it on the Go Playground):
int32 value: 1
int32 value: 2
Upvotes: 1
Reputation: 79586
Is there a way to reduce the allowed types to a specific list of types [of an empty interface]?
At compile time, no.
You may choose to use a limitation at runtime, however. This should be avoided when there are other alternatives, as it doesn't provide as much safety.
This is done in the standard library in a number of places, such as the json marshaler, which requires a pointer as the target.
Upvotes: 2