具有不同运行时类型的通用容器

Rea*_*soN 3 generics channel go

我目前正在尝试通过通道将数据发送到 goroutine,然后 goroutine 会进一步处理它。我的问题是我希望通道能够适用于任何类型。为此,我研究了 Go 1.18 中新引入的泛型。

我的问题是,当我启动 goroutine 时,我需要告诉它通道的类型,这是不可能告诉的,因为它可以保存任何数据。

这就是我现在得到的:

线:

func StartController[T any](sender chan Packet[T]) {
    go runThread(sender)
}

func runThread[T any](sender chan Packet[T]) {
    fmt.Println("in thread")
    for true {
        data := <- sender

        fmt.Println(data)
    }
}
Run Code Online (Sandbox Code Playgroud)

我的测试功能是这样的:

func main() {
    sender := make(chan Packet)

    StartController(sender)

    sender <- Packet[int]{
        Msg: Message[int]{
            Data: 1,
        },
    }

    sender <- Packet[string]{
        Msg: Message[string]{
            Data: "asd",
        },
    }

    for true {}
}
Run Code Online (Sandbox Code Playgroud)

类型:

type Message[T any] struct {
    Data T
}

type Packet[T any] struct {
    Msg Message[T]
}
Run Code Online (Sandbox Code Playgroud)

现在这段代码无法编译,因为

.\test.go:8:22: cannot use generic type Packet[T interface{}] without instantiation
Run Code Online (Sandbox Code Playgroud)

有没有办法正确地做到这一点?

我正在考虑不使用泛型而只使用 interface{} 作为类型,但这会使整个逻辑变得混乱,因为它需要解析(甚至可能不可能,因为数据可能相当复杂(嵌套结构))

bla*_*een 9

这是使用泛型的错误方式。

参数化类型(例如,chan T必须先使用具体类型参数实例化,然后才能使用它)。给定一个定义的 chan 类型:

type GenericChan[T any] chan T
Run Code Online (Sandbox Code Playgroud)

您仍然需要使用具体类型实例化它:

c := make(GenericChan[int])
Run Code Online (Sandbox Code Playgroud)

这使得使用类型参数有点没有意义。

我不知道你的背景是什么,但考虑一下泛型长期以来一直稳定存在的语言。例如爪哇。并考虑典型的 Java 泛型收集器List<T>。您通常做的是使用类型实例化它:

type GenericChan[T any] chan T
Run Code Online (Sandbox Code Playgroud)

您在这里尝试做的是声明一个可以采用任何类型的通道。在 Java 中,什么是可以容纳任何类型的列表?

c := make(GenericChan[int])
Run Code Online (Sandbox Code Playgroud)

在 Go 中,这无非是

c := make(chan interface{})
Run Code Online (Sandbox Code Playgroud)

您可以用另一种方式看待它:您希望这个通用 chan 如何处理接收操作?

c := make(GenericChan) // wrong syntax: instantiating without type param
c <- "a string"        // let's pretend you can send anything into it

// ...

foo := <-c 
Run Code Online (Sandbox Code Playgroud)

此时什么是foo?是一个吗string?或者一个int?您可以将任何内容发送到其中。这就是为什么像您的示例中那样的通用 chan 无法按您预期的方式工作。它必须是chan interface{},然后您可以像现在没有泛型一样键入断言收到的项目。

泛型的要点是编写使用任意类型的代码,同时保持类型安全:

func receiveAny[T any](c chan T) T {
    return <-c
}
Run Code Online (Sandbox Code Playgroud)

您可以使用 achan int或 a来调用它chan string