不能使用 (type []byte) 作为类型 io.Reader

Cannot use (type []byte) as type io.Reader

我没看懂错误,这是我在本机执行的main.go"A":

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)
}

我在golang pkg中看到了io.Reader的文档:

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

为什么我会收到错误...

...当我做 go install ?

我认为当您认为 []byte 等同于 Reader 只是因为 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.

简短回答:将您的缓冲区包装在 Reader type by using bytes.NewReader

或者,您可以使用 bytes.NewBuffer 来达到类似的效果。

如果源是字符串,可以使用strings.NewReader.

读者名单不断:https://golang.org/search?q=Read#Global


更深层问题的解释

更深层的问题是:为什么数组不直接支持 io.Reader 接口?

io.Reader 支持从通用数据流中读取的概念,不一定事先知道总大小。为了支持这一点,重复调用 Read 直到所有输入数据都用完。在许多语言中,必须至少调用两次类似的读取函数,其中最后一次调用 returns 一个标志,指示 文件结束 .

通过返回两个值(其中一个是 error 类型),Go 可以一次性完成数组读取,但前提是目标缓冲区很大足以消耗所有可用数据——这并不总是事先知道的。

io.Reader 接口指定了 Read() 函数的签名和行为:

func (T) Read(b []byte) (n int, err error)

Read populates the given byte slice with data and returns the number of bytes populated and an error value. It returns an io.EOF error when the stream ends.

因此,由于 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 参数是目标缓冲区,而不是源缓冲区。