在Go中传递一个通道作为接口通道

时间:2014-03-25 01:13:42

标签: generics interface go channel

我的程序有一个管道结构,我刚刚实现了一个缓存过滤器,如果已处理的数据版本在缓存中,它会直接将内容发送到输出。

func Run(in chan downloader.ReadyDownload) chan CCFile {
    out := make(chan CCFile)
    processQueue := make(chan downloader.ReadyDownload)
    go cache.BypassFilter(in, processQueue, out)
        // writes the cached, already processed version to out if it exists
        // otherwise redirects the input to processQueue
    go process(processQueue, out)
    return out
}

问题是我的程序有多个这样的地方,并且许多类型的结构(如此片段中的ReadyDownload和CCFile)正在通过通道传递。它们都实现了这个接口

type ProcessItem interface {
    Source() string
    Target() string
    Key() string
}

所以我的BypassFilter()函数签名如下所示:

func (c Cache) BypassFilter(in chan ProcessItem, process chan ProcessItem, bypass chan ProcessItem)

但是这会导致以下错误:

cannot use in (type chan downloader.ReadyDownload) as type chan utils.ProcessItem in function argument

虽然ReadyDownload肯定会实现ProcessItem。例如,这没有问题:

foo := downloader.ReadyDownload{}
var bar utils.ProcessItem
bar = foo

所以,我对Go类型和界面的理解非常有限,这让我想问这个问题:它们是的某些东西其他东西,这使得这些类型不兼容?我该怎么做才能让它发挥作用?假设我有一个ReadyDownloads频道。是将数据转发到一个函数的唯一方法,即接口{}的通道作为参数,创建接口{}的新通道,将其传递给函数并从ReadyDownloads的通道读取内容并将它们送到另一个频道?

2 个答案:

答案 0 :(得分:3)

这两种是不同的类型:

processQueue chan ReadyDownload

process chan ProcessItem

您可以在ReadyDownloader类型的通道中放置chan ProcessItem值(如果它实现了接口),但您无法将一种通道类型转换为另一种通道类型,就像您无法转换为[]T切片成[]interface{}切片,这是另一种类似于此的混淆。

您需要做的是制作chan ProcessItem类型的所有渠道:

func Run(in chan ProcessItem) chan CCFile {
    out := make(chan CCFile)
    processQueue := make(chan ProcessItem)
    go cache.BypassFilter(in, processQueue, out)
        // writes the cached, already processed version to out if it exists
        // otherwise redirects the input to processQueue
    go process(processQueue, out)
    return out
}

要详细了解这是为什么(对于切片,但同样适用于通道),您可以阅读以下go-wiki页面:

http://code.google.com/p/go-wiki/wiki/InterfaceSlice

答案 1 :(得分:0)

将每个频道更改为struct个频道可能会在此处有效,但一般情况下,您可能希望将struct类型视为接口以便进行处理。幸运的是,go给了我们很多解决方案。这是一个。

考虑这个非常简单的设置,我们希望将Object结构类型用作多个接口:

// Get the objects
func ParseFile(fileName string, co chan Object) {
    for _, object := range DoStuff(fileName) {
        co <- object
    }
}

// Use some saving functionality that is defined elsewhere as:
func Archive(cs chan Saveable) {
    for saveable := range cs {
        saveable.Save()
    }
}

type Saveable interface {
    Save()
}

//Implement the interfaces...
func (*Object) Save() {
    fmt.Println("Naa, I'm lazy")
}

// Or some throwing functionality?
func ThrowOnTheWall(ct chan Throwable) {
    for throwable := range cs {
        throwable.Throw()
    }
}

//...

co := make(chan Object)
go ParseFile("file.xml", co)
Archive(co) // Will NOT work, co is of the wrong type.

在这里,使用某些地方chan Object是不合适的,因为你可能想要在墙上扔一些与对象不同的东西(例如type Defecation struct {...},你将它作为Throwable来实现太)。

您可以使用go例程在后台进行投射:

func ObjectToSaveable(from chan Object) chan Saveable {
    to := make(chan Saveable)
    go func() {
        for object := range from {
            to <- &object
        }
        close(to)
    }()
    return to
}

然后用它来封装初始频道:

co := make(chan Object)
go ParseFile("file.xml", co)
Archive(ObjectToSaveable(co))