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

How can I define a struct field in my interface as a type constraint (type T has no field or method)?

我想编译以下代码。通过阅读类型参数提案(Go 泛型),我的理解是这应该可行,但我一定遗漏了一些东西。

package main

import "fmt"

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

func PrintA[T Type](v T) {
    fmt.Printf("%s\n", 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 does not implement Type (possibly missing ~ for struct{A string} in constraint Type) ./prog.go:11:23: v.A undefined (interface Type has no method A)

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

这是已实施提案中的一个示例,它是最新 Go 测试版的一部分。

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

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

The current generics implementation has the following known limitations:

[...]

  • 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.

任何结构类型的变通方法归结为我们迄今为止一直在使用的旧的、无聊的基于接口的多态性,而没有类型参数:

type Type interface {
    GetA() string
}

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

此时您甚至不必使用 Type 接口作为约束。它可以只是一个普通的接口类型:

func PrintA(v Type) {
    fmt.Printf("%s\n", v.GetA())
}

旧答案

在 2022 年初的某个时候,当此功能仍在开发中时,如果您添加 ~:

,您的示例确实有效
type Type interface {
    ~struct{ A string }
}

它仅适用于 完全 定义为 struct{ A string } 的结构,除此之外别无他法。定义一个约束“代表[s]具有特定类型的特定字段的所有结构”从来没有被支持过。有关详细信息,请参阅

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

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 月起,此代码不再编译。