Interfaces as type constraints
May 5, 2023
If you’ve been playing around with interface types while reading about them for the last couple of weeks, you have probably noticed something: Many of the interface types described don’t work as variables. Here we see why:
General interfaces
…
Interfaces that are not basic 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.
var x Float // illegal: Float is not a basic interface var x interface{} = Float(nil) // illegal type Floatish struct { f Float // illegal }
This makes for a potentially confusing situation: We can define a type that cannot be used as a variable.
So then what’s the point of such a type?
Well, they’re useful as type constraints in generic functions which, we’ll dig into later. But as a preview/refresher, here’s what that looks like:
type Int interface {
int | int8 | int16 | int32 | uint | uint8 | uint16 | uint32
}
func add[T Int](a, b T) T {
return a + b
}
So if that Int
type cannot represent a variable, then of what type are the arguments a
and b
inside the above function?
Well, that depends on the type passed into the function! Let’s add a bit of debugging output to the function:
func add[T Int](a, b T) T {
fmt.Printf("%T\n", a) // %T prints the argument's type
return a + b
}
Now let’s call it with different input types and see what it does:
add(int(3), int(4)) // Prints: int
add(int32(5), int32(6)) // Prints: int32
Quotes from The Go Programming Language Specification Version of December 15, 2022