无法使用 (类型 []byte) 作为类型 io.Reader。

13

我不理解这个错误,这是我在"A"机器上执行的main.go文件:

package main

import (
    "fmt"
    "net"
    "os"
    "github.com/mistifyio/go-zfs"
)

func main() {
    // Listen for incoming connections.
    l, err := net.Listen("tcp", "192.168.99.5:9977")
    if err != nil ...
    // Close the listener when the application closes.
    defer l.Close()
    fmt.Println("Listening on " + CONN_HOST + ":" + CONN_PORT)
    for {
        // Listen for an incoming connection.
        conn, err := l.Accept()
        if err != nil ...

        //Handle connections in a new goroutine.
        go handleRequest(conn)
    }
}

// Handles incoming requests.
func handleRequest(conn net.Conn) {
    // Make a buffer to hold incoming data.
    buff := make([]byte, 1024)
    // Read the incoming connection into the buffer.
    _, err := conn.Read(buff)
    if err != nil {
        fmt.Printf("Error reading: %s.\n", err.Error())
    }
    // ReceiveSnapshot
    ds, err := zfs.ReceiveSnapshot(buff, "tank/replication")
    if err != nil {
        fmt.Printf("Error receiving: %s.\n", err.Error())
    }
    fmt.Printf("%s... done!\n", ds)
    // Send a response back to person contacting us.
    conn.Write([]byte("Received!"))
    // Close the connection when you're done with it.
    conn.Close()
}

现在,我向您展示来自github.com/mistifyio/go-zfs/zfs.go的ReceiveSnapshot函数:

type command struct {
    Command string
    Stdin   io.Reader
    Stdout  io.Writer
}

func ReceiveSnapshot(input io.Reader, name string) (*Dataset, error) {
    c := command{Command: "zfs", Stdin: input}
    _, err := c.Run("receive", name)
    if err != nil {
        return nil, err
    }
    return GetDataset(name)
}

我在 Go 语言的 pkg 中看到了 io.Reader 的文档:

type Reader interface {
        Read(p []byte) (n int, err error)
}

当我执行 go install 命令时,为什么会出现以下错误:

  • 无法将 buff(类型 []byte)作为参数传递给 zfs.ReceiveSnapshot 的类型 io.Reader: []byte 未实现 io.Reader(缺少 Read 方法)

5
这个错误消息的含义是什么?一个[]byte类型的变量没有实现io.Reader接口的Read()方法,因此不能被用作io.Reader。请参考Go语言教程中“方法与接口”章节学习相关知识(从这里开始:https://tour.golang.org/methods/1)。 - Volker
1
我知道信息很清楚,但如果我问这个问题是因为我不理解(而且我已经阅读了指南...)buff := make([]byte)和Read方法Read(p []byte)之间的区别。 - icarbajo
2个回答

22

我认为你在逻辑上缺少了一步,当你认为 []byteReader 相等时,只因为 Reader 的 Read 方法将 []byte 作为参数接收。

让我来尝试澄清一下:

你的 ReceiveSnapshot 函数期望一个 Reader 作为参数:

ReceiveSnapshot( input io.Reader ...

为了让一个类型满足Reader接口,该类型本身应实现此函数:

Read(p []byte) (n int, err error)

请注意,类型应该实现该函数才能成为Reader

[]byte 没有实现 Read 函数。只是偶然的是,Read 的参数恰好是一个[]byte

为了使其正常工作,您需要向ReceiveSnapshot发送一个正确的Reader

幸运的是,拥有[]byte并希望对其进行读取是一种常见情况,因此API提供了一种简单的方法:

https://golang.org/pkg/bytes/#NewReader

您只需要将bytes.NewReader(buff)发送到您的ReceiveSnapshot函数中,而不是仅发送buff


非常感谢!@eugenioy - icarbajo
我一直回到这个非常有帮助的答案,非常感谢 @eugenjoy。 - citizenrich

6
简短回答:使用bytes.NewReader将您的缓冲区包装在Reader类型中,或者您可以使用bytes.NewBuffer来达到类似的效果。如果源是字符串,则可以使用strings.NewReader。阅读器列表不胜枚举:https://golang.org/search?q=Read#Global 更深层次问题的解释:为什么数组不直接支持io.Reader接口?io.Reader支持从一般数据流中读取,其中总大小未必事先知道。为了支持这一点,调用Read函数重复读取数据,直到所有输入数据耗尽。在许多语言中,类似的读取函数必须至少调用两次,最后一次呈现指示文件末尾的标志。通过返回两个值(其中之一是类型为error的错误),Go使得数组的读取可以一次完成,但前提是目标缓冲区足够大以消耗所有可用数据,这并不总是事先知道的。io.Reader接口指定了Read()函数的签名和行为:
func (T) Read(b []byte) (n int, err error)

Read方法会填充给定的字节切片并返回填充的字节数以及一个错误值。当流结束时,它会返回一个 io.EOF 错误。

由于 io.Reader 接口的工作方式,一个简单的字节缓冲区无法实现它。为了记住后续对 Read() 的调用之间的状态,需要使用一个包装器结构。

出于兴趣,以下是一个示例,显示了如何实现它...

type MyReader struct {
    src []byte
    pos int
}

func (r *MyReader) Read(dst []byte) (n int, err error) {
    n = copy(dst, r.src[r.pos:])
    r.pos += n
    if r.pos == len(r.src) {
        return n, io.EOF
    }
    return
}

func NewMyReader(b []byte) *MyReader { return &MyReader{b, 0} }

注意,Read() 方法中的 []byte 参数是目标缓冲区,而不是源缓冲区。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接