如何编写一个通用的 Go 函数,允许指向多种原始类型的指针?

huangapple go评论90阅读模式
英文:

How do you write a generic Go function that allows pointers to multiple primitive types?

问题

我正在尝试使用Go泛型来编写一个函数,以减少我们代码中一些样板化的if/else块。我想出了一个只适用于单个类型参数的解决方案,如下所示:

func valueOrNil[T *int](value T) any {
	if value == nil {
		return nil
	}
	return *value
}

虽然这个方法可以正常工作,但它并不是真正有用,因为它只允许*int类型,而我希望这段代码适用于任何原始类型。我尝试扩展它以支持第二个类型,如下所示:

func valueOrNil[T *int | *uint](value T) any {
	if value == nil {
		return nil
	}
	return *value
}

然而,这个变体在编译时出现错误:

invalid operation: pointers of value (variable of type T constrained by *int|*uint) must have identical base types

有人能发现我在这里做错了什么吗?还是这种方式只是"不支持"的?

英文:

I'm trying to use Go generics to write a function to cut down some of the boilerplate if/else blocks in our code. I came up with something that works for a single type parameter as follows:

func valueOrNil[T *int](value T) any {
	if value == nil {
		return nil
	}
	return *value
}

While this works fine, it's not really useful since it only allows *int, and I want this code to work with any primitive type. I tried to extend that to support a second type, as follows:

func valueOrNil[T *int | *uint](value T) any {
	if value == nil {
		return nil
	}
	return *value
}

However, this variation fails with a compiler error:

invalid operation: pointers of value (variable of type T constrained by *int|*uint) must have identical base types

Can anyone spot what I'm doing wrong here, or is something like this just "not supported"?

答案1

得分: 5

问题似乎在于你试图对指针类型进行泛型化,而不是对类型本身进行泛型化。如果我们将指针移到参数本身而不是类型参数上,它就可以工作了。

下面是解释,但这是可行的代码:

func valueOrNil[T ~int | ~uint](value *T) T {
    if value == nil {
        var zero T
        return zero
    }
    return *value
}

所以,不要使用下面这种方式(它不起作用):

func valueOrNil[T *int | *uint](value T) any

你可以这样做:

func valueOrNil[T int | uint](value *T) any

然而,你可能希望进一步处理底层类型:

func valueOrNil[T ~int | ~uint](value *T) any

这将允许使用自定义类型与该函数一起使用:

type Thing int

var thing Thing
println(valueOrNil(thing))

另一个你可能要考虑的方面是对返回类型进行泛型化。你可以使用相同的T参数来实现这一点。

例如:

func valueOrNil[T ~int | ~uint](value *T) T

但这意味着你需要更改部分实现。不要使用下面这种方式:

if value == nil {
    return nil
}

你可以尝试这样做:

if value == nil {
    var zero T
    return zero
}
英文:

The trouble seems to be that you're trying to be generic over a pointer to the type rather than the type itself. If we move the pointer to be on the parameter itself rather than the type parameter, it works.

Explanation is below, but here is the working code:

func valueOrNil[T ~int | ~uint](value *T) T {
    if value == nil {
        var zero T
        return zero
    }
    return *value
}

So instead of this (which doesn't work):

func valueOrNil[T *int | *uint](value T) any

You could do this:

func valueOrNil[T int | uint](value *T) any

However, you may want to take it a step further and handle underlying types:

func valueOrNil[T ~int | ~uint](value *T) any

This would allow a custom type to be used with the function:

type Thing int

var thing Thing
println(valueOrNil(thing))

Another facet you may want to consider is being generic over the return type as well. You can do so using the same T parameter.

For example:

func valueOrNil([T ~int | ~uint](value *T) T

But this means you would need to change part of the implementation. Instead of this:

if value == nil {
    return nil
}

You could do something like this:

if value == nil {
    var zero T
    return zero
}

答案2

得分: 0

我接受的答案(来自Miquella)让我意识到,实际上我在将*移到参数类型上时不需要指定特定的类型,所以这就是我最终得到的结果

func valueOrNil[T any](value *T) any {
	if value == nil {
		return nil
	}
	return *value
}
英文:

The answer I accepted from Miquella (thanks for the insights; I learned something about the usage of ~, too) made me realize that I don't actually need to specify a specific type once I move the * onto the parameter type, so this is what I ended up with:

func valueOrNil[T any](value *T) any {
	if value == nil {
		return nil
	}
	return *value
}

huangapple
  • 本文由 发表于 2022年3月26日 05:55:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/71623524.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定