英文:
Return default value for generic type
问题
如何在泛型类型 T
中返回 nil
?
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
}
我得到了以下编译错误:
无法在返回语句中使用 nil 作为 T 类型的值
英文:
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
答案1
得分: 70
你不能对于任何类型返回nil
。例如,如果将int
用作T
的类型参数,返回nil
是没有意义的。对于结构体来说,nil
也不是一个有效的值。
你可以做的是返回用于T
类型参数的零值,这是有意义的。例如,对于指针和切片来说,零值是nil
,对于string
来说是空字符串,对于整数和浮点数来说是0
。
如何返回零值?只需声明一个T
类型的变量,并返回它:
func getZero[T any]() T {
var result T
return result
}
测试一下:
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)
输出结果为(在Go Playground上尝试一下):
int 0
string ""
image.Point (0,0)
*float64 <nil>
英文:
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>
答案2
得分: 63
*new(T)
习惯用法
这被建议作为 golang-nuts 中首选的选项。虽然可读性可能较差,但如果/当某个零值内置类型被添加到语言中时,更容易找到并替换。
它还允许一行代码完成赋值。
new
内置函数 为任何类型的变量分配存储空间并返回指向它的指针,因此对 *new(T)
解引用实际上得到了 T
的零值。你可以使用类型参数作为参数:
func Zero[T any]() T {
return *new(T)
}
如果 T
是可比较的,这在检查某个变量是否为零值时非常方便:
func IsZero[T comparable](v T) bool {
return v == *new(T)
}
类型为 T
的 var
直接明了,阅读起来更容易,尽管总是需要多一行代码:
func Zero[T any]() T {
var zero T
return zero
}
命名返回类型
如果你不想显式声明一个变量,可以使用命名返回。虽然不是每个人都喜欢这种语法,但在函数体比这个假设的例子更复杂,或者如果你需要在 defer
语句中操作该值时,这可能会很方便:
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>
}
命名返回的语法与变量声明的语法非常相似,这并非偶然。
使用你的例子:
func (list *mylist[T]) pop() (data T) {
if list.first != nil {
data = list.first.data
list.first = list.first.next
}
return
}
对于不可为 nil 的类型返回 nil
如果你确实想要这样做,如你在问题中所述,你可以显式地返回 *T
。
当类型参数 T
受限于不包括指针类型的某些约束时,可以将返回类型声明为 *T
,现在你可以返回 nil
,它是指针类型的零值。
// 约束仅包括非指针类型
func getNilFor[T constraints.Integer]() *T {
return nil
}
func main() {
fmt.Println(reflect.TypeOf(getNilFor[int]())) // *int
fmt.Println(reflect.TypeOf(getNilFor[uint64]())) // *uint64
}
让我再次强调:当 T
不受限于任何允许指针类型的约束时,这种方法效果最好,否则你得到的是指向指针的指针类型:
// 注意这一点
func zero[T any]() *T {
return nil
}
func main() {
fmt.Println(reflect.TypeOf(zero[int]())) // *int,好的
fmt.Println(reflect.TypeOf(zero[*int]())) // **int,也许不是你想要的...
}
英文:
The *new(T)
idiom
This 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
}
Named return types
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
}
<hr>
Return nil
for non-nillable types
If 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...
}
答案3
得分: 0
你可以初始化一个空变量。
if l == 0 {
var empty T
return empty, errors.New("empty Stack")
}
请注意,这是一个示例代码片段,其中的变量类型 T
需要根据实际情况进行替换。
英文:
You can init a empty variable.
if l == 0 {
var empty T
return empty, errors.New("empty Stack")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论