I'm about to migrate existing code to use go generics. This very basic example shows the problem:
// Just an interface
type Object interface {
Test() error
}
// A func returning an interface can return `nil`
func Get() Object {
return nil
}
// Why I can't return `nil` from a generic func constrained to `interface Object`?
func GetG[T Object]() T {
return nil // ./prog.go:15:9: cannot use nil as T value in return statement
}
https://go.dev/play/p/3WTg1aYDzCA
If func Get
, which returns interface Object
is allowed to return nil
, why is the generic version func GetG
where T is constrained to Object
not allowed to do so?
Can anyone tell me more about the behaviour or the rational behind it?
A type parameter is not its constraint.
nil
may not be assignable to the concrete type argumentIntuitively, let's say I implement your interface with a defined integer type:
type Foo int8
func (Foo) Test() error { return nil }
Does Foo
satisfy the constraint Object
? Yes, it implements the method. Can I instantiate GetG
with Foo
? Yes, it satisfies the constraint. Can I assign nil
to Foo
? No, I can't:
When storage is allocated for a variable, [...] the variable or value is given a default value. [...]
nil
for pointers, functions, interfaces, slices, channels, and maps.
Therefore, you can't always return nil
if the return type is a type parameter. You may return nil
in other circumstances:
T
, it's *T
.func GetG[T Object]() *T {
return nil // ok
}
nil
to all types in the type parameter's type set — identified by its constraint:// type set comprises only *int and *string
func GetG[T *int | *string]() T {
return nil // ok
}
The case above is mentioned by the language spec in Assignability:
A value
x
of typeV
is assignable to a variable of typeT
[...] if[...]
- x is the predeclared identifier
nil
, T is a type parameter, and x is assignable to each type in T's type set.
With a basic interface (methods only) as a constraint, this condition doesn't hold for the reason explained above: you can satisfy it with types which nil
isn't assignable to.
Otherwise what you can do is to return T
's zero value, which you can do in several different ways, the most straightforward one is:
func GetG[T Object]() T {
var zero T
return zero
}