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.