Reputation: 835
How do you return nil
for a generic type T
?
func (list *mylist[T]) pop() T {
if list.first != nil {
data := list.first.data
list.first = list.first.next
return data
}
return nil
}
func (list *mylist[T]) getfirst() T {
if list.first != nil {
return list.first.data
}
return nil
}
I get the following compilation error:
cannot use nil as T value in return statement
Upvotes: 78
Views: 38191
Reputation: 44665
*new(T)
idiomThis has been suggested as the preferred option in golang-nuts. It is probably less readable but easier to find and replace if/when some zero-value builtin gets added to the language.
It also allows one-line assignments.
The new
built-in allocates storage for a variable of any type and returns a pointer to it, so dereferencing *new(T)
effectively yields the zero value for T
. You can use a type parameter as the argument:
func Zero[T any]() T {
return *new(T)
}
In case T
is comparable, this comes in handy to check if some variable is a zero value:
func IsZero[T comparable](v T) bool {
return v == *new(T)
}
var
of type T
Straightforward and easier to read, though it always requires one line more:
func Zero[T any]() T {
var zero T
return zero
}
If you don't want to explicitly declare a variable you can use named returns. Not everyone is fond of this syntax, though this might come in handy when your function body is more complex than this contrived example, or if you need to manipulate the value in a defer
statement:
func Zero[T any]() (ret T) {
return
}
func main() {
fmt.Println(Zero[int]()) // 0
fmt.Println(Zero[map[string]int]()) // map[]
fmt.Println(Zero[chan chan uint64]()) // <nil>
}
It's not a chance that the syntax for named returns closely resembles that of var declarations.
Using your example:
func (list *mylist[T]) pop() (data T) {
if list.first != nil {
data = list.first.data
list.first = list.first.next
}
return
}
nil
for non-nillable typesIf you actually want to do this, as stated in your question, you can return *T
explicitly.
This can be done when the type param T
is constrained to something that excludes pointer types. In that case, you can declare the return type as *T
and now you can return nil
, which is the zero value of pointer types.
// constraint includes only non-pointer types
func getNilFor[T constraints.Integer]() *T {
return nil
}
func main() {
fmt.Println(reflect.TypeOf(getNilFor[int]())) // *int
fmt.Println(reflect.TypeOf(getNilFor[uint64]())) // *uint64
}
Let me state this again: this works best when T
is NOT constrained to anything that admits pointer types, otherwise what you get is a pointer-to-pointer type:
// pay attention to this
func zero[T any]() *T {
return nil
}
func main() {
fmt.Println(reflect.TypeOf(zero[int]())) // *int, good
fmt.Println(reflect.TypeOf(zero[*int]())) // **int, maybe not what you want...
}
Upvotes: 90
Reputation: 41
You can init an empty variable.
if l == 0 {
var empty T
return empty, errors.New("empty Stack")
}
Upvotes: 2
Reputation: 417592
You can't return nil
for any type. If int
is used as the type argument for T
for example, returning nil
makes no sense. nil
is also not a valid value for structs.
What you may do–and what makes sense–is return the zero value for the type argument used for T
. For example the zero value is nil
for pointers, slices, it's the empty string for string
and 0
for integer and floating point numbers.
How to return the zero value? Simply declare a variable of type T
, and return it:
func getZero[T any]() T {
var result T
return result
}
Testing it:
i := getZero[int]()
fmt.Printf("%T %v\n", i, i)
s := getZero[string]()
fmt.Printf("%T %q\n", s, s)
p := getZero[image.Point]()
fmt.Printf("%T %v\n", p, p)
f := getZero[*float64]()
fmt.Printf("%T %v\n", f, f)
Which outputs (try it on the Go Playground):
int 0
string ""
image.Point (0,0)
*float64 <nil>
Upvotes: 101