使用Go 1.18泛型,如何将约束类型用作期望具体类型的函数的参数?

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

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

问题

Go版本:1.18

这是一个不太有用的愚蠢示例。我正在使用它作为学习泛型的练习。

我有一个Pokemon接口

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

和一个实现了Pokemon接口的带有类型参数的Charmander

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)
}

我的编译器报错了

> 无法将c.AttackPower(类型为float64,受Float约束的变量)作为参数传递给other.ReceiveDamage函数中的float64值编译器(IncompatibleAssign)

我已经将结构体泛型实例化为*Charmander[float64]。我期望编译器知道AttackPower是一个float64类型。

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

英文:

Go version: 1.18

Here's a silly example that is not particularly useful. I am using this as an exercise to learn generics.

I have a Pokemon interface

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

and Charmander with type parameter that implements the Pokemon interface.

type Float interface {
	float32 | float64
}

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

I want to use Charmander's attack power to inflict damage.

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

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

My compiler gives error

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

I already instantiated the struct generic as *Charmander[float64]. I'd expect that the compiler knows AttackPower is a float64.

When I pass a float64 into a function that expects float64, why should it complain? On the other hand, ReceiveDamage does not complain. I am subtracting a float64 from Health which is a constrained type.

答案1

得分: 3

你必须使用类型转换。ReceiveDamage 方法期望一个 float64 类型,但主类型是参数化的 F。即使约束为仅限于浮点数,或者仅限于特定的浮点数,它也不是 float64 类型。它是 F 类型(此外,它也可以实例化为 float32)。

两种转换都可以编译,因为 float64 可以转换为类型参数的类型集中的所有类型,即 float32float64,反之亦然。

因此,方法变为:

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

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

修复后的 playground:https://go.dev/play/p/FSsdlL8tBLn

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

英文:

You have to use type conversions. The method ReceiveDamage expects a float64 but the main type is parametrized in F. Something of type F, even if constrained to floats only, or even if constrained to one specific float, is not float64. It is F. (Moreover, it could also be instantiated with float32).

Both conversions compile because float64 is convertible to all types in the type parameter's type set, float32 and float64, and vice-versa.

So the methods become:

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

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

Fixed playground: https://go.dev/play/p/FSsdlL8tBLn

Watch out that the conversion T(damage) may cause loss of precision when T is instantiated with float32. (Probably this won't be an issue in this specific use case...)

huangapple
  • 本文由 发表于 2022年3月16日 08:21:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/71490463.html
匿名

发表评论

匿名网友

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

确定