奇怪的 Go 行为 - 泛型结构返回

问题描述 投票:0回答:1

尝试在 Go 中重新创建 Java Options(出于学习目的)。有趣的是,直接返回值与通过变量赋值返回值有不同的行为,一个可以工作,另一个则不能。 在 Of 方法中,当我返回

EmptyOptional
时,我收到编译时错误,但是当我返回
&optional[TT]{}
时,它的工作正常。

package utils

type TT interface{}
type Optional[T TT] interface {
    Of(t T) Optional[T]
    Map(func() Optional[T]) Optional[T]
}

var EmptyOptional = &optional[TT]{}

type optional[T any] struct {
    t T
}

func (o *optional[TT]) Of(t TT) Optional[TT] {
    if t == nil {
//      return &optional[TT]{} // <<-- Working 
        return EmptyOptional   // <<-- Not Working 
    }
    return nil
}
func (o *optional[TT]) Map(func() Optional[TT]) Optional[TT] {
    return nil

}

编译时错误

./prog.go:24:10: cannot use EmptyOptional (variable of type *optional[TT]) as Optional[TT] value in return statement: *optional[TT] does not implement Optional[TT] (wrong type for method Map)
        have Map(func() main.Optional[main.TT]) main.Optional[main.TT]
        want Map(func() main.Optional[TT]) main.Optional[TT]

今天是第一周。我尽力理解这种行为叫什么。

go generics
1个回答
0
投票

你遇到的问题比你承认的还要多。将您的代码放在游乐场中,编译器无法访问您声称“有效”的代码,因为它包含在具有无效谓词的

if
子句中:

invalid operation: t == nil (mismatched types TT and untyped nil)

在 Go Playground 中创建 MVCE 可以帮助消除此类问题,并呈现一个人们可以参与并致力于帮助解决您的问题的场景(这样做可能会帮助您自己认识到问题)。

这里是一个包含说明该问题的初始代码的游乐场。

抛开这个问题,并忽略这样一个事实:尝试在 Go 中重新创建 Java 选项对于学习练习来说没有什么价值......

问题

EmptyOptional

 是实例化 
optional[T]
 类型的值,其中 
T
 属于 
TT
 类型(与现有 
any
 类型完全相同的类型的冗余重新声明)。

Of

方法是泛型类型
Optional[T]
的方法。 
Of
 可以返回 
EmptyOptional
 的唯一场景是 
Optional[T]
 的类型参数与 
EmptyOptional
 的实例类型相同的特定情况:即 
TT
.

但是由于

Of

 方法必须返回泛型类型的值,因此尝试返回某些具体类型的特定值是不可能的。

简化以澄清

您选择的符号并不能帮助您理解您所创建的问题。这个替代方案可能会有所帮助:

package utils type Optional[T any] interface { Of(t T) Optional[T] } var EmptyOptional = &optional[any]{} type optional[T any] struct { t T } func (o *optional[T]) Of(t T) Optional[T] { //return &optional[T]{} // <<-- Working return EmptyOptional // <<-- Not Working: a value of concrete type is never compatible with a generic }
如果您愿意,这也可以在游乐场中进行进一步探索。

在此简化版本中,我删除了非法且存在不同问题的 if

声明。我还删除了多余的

TT

 接口和不必要的 
Map
 方法,因为这些与问题完全无关。

从这个简化的例子中可以明显看出,

EmptyOptional

,它是

具有特定类型参数的泛型类型的具体实例化

any)不能在Optional[T]
(即尚未实例化)的地方使用)通用。
考虑这个假设:

type OptionalInt optional[int]

这会使用返回
Of[int]

Optional[int]

 方法实例化一个类型,但 
EmptyOptional
 的类型为 
Optional[any]
补充

即使使用相同的类型参数实例化,实例化的泛型类型也不兼容;它们始终是不同的类型,如

最后一个游乐场所示

type generic[T any] struct{ v T } type genericInt generic[int] type otherGenericInt generic[int] func main() { a := genericInt{42} aa := genericInt{42} fmt.Println(a == aa) // this is OK: a and aa are of the same type, supporting comparable: evaluates as 'true' // neither of these declarations of b yields a value that is comparable with a, // being of different types to a in both cases b := generic[int]{42} // b := otherGenericInt{42} fmt.Println(a.v == b.v) // this is fine, but... fmt.Println(a == b) // ERROR: mismatched types }

简而言之:GoLang 不是 Java;它也不是 JavaScript。 :)
该怎么办?

在 GoLang 中实现可选性(行为,而不是类型)的惯用方法是简单地使用指针,并用

nil

标识

not present

 属性。泛型不涉及此。
习惯了这个,你会发现它更简单。

© www.soinside.com 2019 - 2024. All rights reserved.