使用 Go 1.18 泛型,如何使用约束类型作为需要具体类型的函数的参数?

With Go 1.18 generics, how to use constrained type as argument to function that expects a concrete type?

转到版本:1.18

这是一个不是特别有用的愚蠢示例。我将其用作学习泛型的练习。

我有一个Pokemon界面

type Pokemon interface {
    ReceiveDamage(float64)
    InflictDamage(Pokemon)
}

Charmander 具有实现 Pokemon 接口的类型参数。

type Float interface {
    float32 | float64
}

type Charmander[F Float] struct {
    Health      F
    AttackPower F
}

我想用Charmander的攻击力造成伤害

func (c *Charmander[float64]) ReceiveDamage(damage float64) {
    c.Health -= damage
}

func (c *Charmander[float64]) InflictDamage(other Pokemon) {
    other.ReceiveDamage(c.AttackPower)
}

我的编译器报错

cannot use c.AttackPower (variable of type float64 constrained by Float) as float64 value in argument to other.ReceiveDamage compiler(IncompatibleAssign)

我已经将泛型结构实例化为 *Charmander[float64]。我希望编译器知道 AttackPowerfloat64.

当我将 float64 传递给需要 float64 的函数时,它为什么会抱怨?另一方面,ReceiveDamage 并没有抱怨。我从 Health 中减去 float64,这是一个受约束的类型。

你必须使用类型转换。方法 ReceiveDamage 需要一个 float64 但主要类型在 F 中被参数化。 F 类型的东西,即使仅限于浮点数,或者即使仅限于一个特定的浮点数, 也不是 float64。即F。 (此外,它也可以用 float32 实例化)。

两种转换都可以编译,因为 float64 可以转换为类型参数类型集中的所有类型,float32float64,以及 vice-versa.

所以方法变成:

func (c *Charmander[T]) ReceiveDamage(damage float64) {
    c.Health -= T(damage)
}

func (c *Charmander[T]) InflictDamage(other Pokemon) {
    other.ReceiveDamage(float64(c.AttackPower))
}

固定游乐场:https://go.dev/play/p/FSsdlL8tBLn

T 实例化为 float32 时,请注意转换 T(damage) 可能会导致精度损失。 (在这个特定用例中这可能不是问题...)