通过类型参数限制的指针求和

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

Sum by pointers which are constrained by type parameters

问题

这段代码的问题在于无法将指针类型作为类型参数进行约束。在第二个示例中,指针类型的约束是不允许的,因此代码无法编译通过。在第三个示例中,虽然定义可以编译通过,但是对函数的调用却无法通过编译,因为*int64并不实现*int32。第四个示例是可以正常工作的。

关于你的问题:

  • *Numeric不被允许,但是T0 Numeric, T1 *T0是可以的,这是符合预期的。
  • 除了使用第四个示例中的方法,目前没有更简单的方式了。
英文:

This code works fine:

type Numeric interface {
	int32 | int64
}

func SumByPointer[T1 Numeric, T2 Numeric](v1 *T1, v2 *T2) {
    *v1 = *v1 + T1(*v2)
}

I want the pointer types to be constrained by type parameters. This is not compiled:

// Err: ./prog.go:11:23: interface contains type constraints    
func SumByPointer[T1 *Numeric, T2 *Numeric](v1 T1, v2 T2) {

Here definition is compiled but call to function is not:

func SumByPointer[T0 Numeric, T1 *T0, T2 *T0](v1 T1, v2 T2) {
....
// Err: ./prog.go:19:15: *int64 does not implement *int32
SumByPointer(&v1, &v2)

This works:

func SumByPointer[T0 Numeric, T02 Numeric, T1 *T0, T2 *T02](v1 T1, v2 T2) {

Questions:

  • Is this expected that *Numeric is not allowed but T0 Numeric, T1 *T0 works?

  • Is there easier way than latter one?

答案1

得分: 2

首先要注意的是,整数之间的转换可能会导致截断。特别是,如果你依赖于调用点处的类型推断,那么你的代码可能会出现微妙的错误,因为根据函数参数的顺序,截断可能会发生。

明确一点:

v1 := int32(10)
v2 := int64(20)
SumByPointer(&v1, &v2)
SumByPointer(&v2, &v1)

这两个调用都是合法的,但第一个调用将int64转换为int32,可能会发生截断。有了这个前提:

> 这是预期的,*Numeric 不允许吗?

是的。你的约束Numeric包括一个类型项,具体来说是并集int32 | int64,这使得它成为一个非基本接口,而非基本接口只能用作类型约束。

当你写T1 *Numeric时,Numeric不再被用作约束。它被用作类型字面量的一部分(一个指针)。如果你考虑到T1 *Numeric实际上是以下语法糖:

T1 interface{ *Numeric }

当你写T0 Numeric, T1 *T0时:

  • Numeric再次被用作T0的约束
  • T0不是一个接口,它是一个类型参数
  • 你可以将T0作为类型字面量*T0的一部分,然后将其用作T1的约束——相当于T1 interface { *T0 }

> 有比后者更简单的方法吗?

你的第一个尝试已经很简单了,我建议你坚持使用它:

func SumByPointer[T1 Numeric, T2 Numeric](v1 *T1, v2 *T2) {
    *v1 = *v1 + T1(*v2)
}

将指针隐藏在类型参数后面并没有实际带来太多好处。实际上,为了使加法编译通过,你必须将指针排除在并集之外。而为了使转换有效,你必须使用基本类型。所以根据我所见,你的最终尝试也是有些被迫的。

另一种可能的方法是将Numeric参数化:

type Numeric[T int32 | int64] interface {
	*T
}

func SumByPointer[T1 Numeric[N], T2 Numeric[M], N, M int32 | int64](v1 T1, v2 T2) {
	*v1 = *v1 + N(*v2)
}

不过,是否认为这种方法更"简单"完全取决于你。从实际角度来看,Numeric接口仍然是非基本的。

英文:

As a foreword, note that converting between integers may result in truncation. In particular, your code is open to subtle bugs if you rely on type inference at call sites, because then truncation may occur depending on the order of the function arguments.

To be clear:

v1 := int32(10)
v2 := int64(20)
SumByPointer(&v1, &v2)
SumByPointer(&v2, &v1)

Either call compiles, but the first one converts int64 to int32, which may truncate. With that said:

> Is this expected that *Numeric is not allowed

Yes it is. Your constraint Numeric includes a type term, specifically the union int32 | int64, which makes it a non-basic interface, and non-basic interfaces can only be used as type constraints.

When you write T1 *Numeric, Numeric is not used as a constraint anymore. It is used as part of a type literal (a pointer). This becomes easier to see if you consider that T1 *Numeric is actually syntactic sugar for:

T1 interface{ *Numeric }

When you write T0 Numeric, T1 *T0 instead:

  • Numeric is again used a constraint of T0
  • T0 is not an interface, it is a type parameter
  • you can use T0 as part of the type literal *T0, which then can be used as the constraint for T1 — equivalent to T1 interface { *T0 }

> Is there easier way than latter one?

Your first attempt is quite easy, I would stick to it:

func SumByPointer[T1 Numeric, T2 Numeric](v1 *T1, v2 *T2) {
    *v1 = *v1 + T1(*v2)
}

Hiding the pointer behind a type parameter doesn't really accomplish much. In fact, for the addition to compile, you have to keep the pointers out of the union. And for the conversion to be valid you have to use the base types. So your final attempt is also sort of forced, as far as I can see.

An alternative might be to parametrize Numeric:

type Numeric[T int32 | int64] interface {
	*T
}

func SumByPointer[T1 Numeric[N], T2 Numeric[M], N, M int32 | int64](v1 T1, v2 T2) {
	*v1 = *v1 + N(*v2)
}

Although whether this appears "easier" is entirely up to you. In practical terms, the Numeric interface is still non-basic.

huangapple
  • 本文由 发表于 2022年4月17日 03:58:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/71897082.html
匿名

发表评论

匿名网友

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

确定