英文:
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
可以转换为类型参数的类型集中的所有类型,即 float32
和 float64
,反之亦然。
因此,方法变为:
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...)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论