如何在接口约束中定义结构字段(类型 T 没有字段或方法)?

我想让下面的代码编译。我从阅读 Type Parameters Proposal (Go Generics) 的理解是这应该可行,但我必须遗漏一些东西。

package main

import "fmt"

func main() {
    s := Struct{A: "Hello World!"}
    PrintA(s)
}

func PrintA[T Type](v T) {
    fmt.Printf("%sn", v.A)
}

type Type interface {
    struct{ A string }
}

type Struct struct {
    A string
}

func (s Struct) String() string {
    return s.A
}

我得到的错误是:

./prog.go:7:8: Struct 没有实现 Type (可能缺少 ~ for struct{A string} in constraint Type) ./prog.go:11:23: v.A undefined (interface Type没有方法A)

我希望 T 代表具有特定类型的特定字段的所有结构。添加 ~ 没有帮助。

这是提案中的一个示例,该示例已实施并且是最新的 Go beta 版本的一部分。

type structField interface {
    struct { a int; x int } |
        struct { b int; x float64 } |
        struct { c int; x uint64 }
}

https://go.dev/play/p/KZh2swZuD2m?v=gotip

stack overflow How can I access a struct field with generics (type T has no field or method)?
原文答案
author avatar

接受的答案

Go 1.18 已完全禁用字段访问。 Go 1.18 release notes 提到了这一点:

当前的泛型实现具有以下已知限制:

[...]

  • The Go compiler does not support accessing a struct field x.f where x is of type parameter type even if all types in the type parameter's type set have a field f. We may remove this restriction in Go 1.19.

The workaround for any struct type boils down to the old boring interface-based polymorphism that we all have been using so far without type params:

type Type interface {
    GetA() string
}

func (s Struct) GetA() string {
    return s.A
}

And at this point you don't even have to use the Type interface as a constraint. It can just be a plain interface type:

func PrintA(v Type) {
    fmt.Printf("%sn", v.GetA())
}

Old answer

At some point in early 2022 while this feature was still in development, your example did work if you added ~:

type Type interface {
    ~struct{ A string }
}

but it only worked for structs exactly defined as struct{ A string } and nothing else. Defining a constraint that "represent[s] all structs with a particular field of a particular type" was never supported all along. See this answer 了解详情。

相反,您从提案中引用的示例是关于访问类型集中的公共字段。通过定义结构的联合:

type structField interface {
    ~struct { a int; x int } | ~struct { a int; x float64 } 
}

应该能够访问这种类型参数的字段 a ,但是正如答案开头所述,这又没有实现。如果联合中的所有术语都具有相同的基础类型,它曾经可以工作(示例改编自 issue #48522 ):

package main

import "fmt"

type Point struct {
    X, Y int
}

type Rect struct {
    X, Y int
}

func GetX[P Point | Rect] (p P) int {
    return p.X
}

func main() {
    p := Point{1, 2}
    r := Rect{2, 3}
    fmt.Println("X: %d %d", GetX(p), GetX(r)) // prints X: 1 2
}

自 2022 年 3 月起,此代码不再编译。


答案: