【问题标题】:Cannot use (type []byte) as type io.Reader不能使用 (type []byte) 作为类型 io.Reader
【发布时间】:2017-10-19 08:11:16
【问题描述】:

我不明白错误,这是我在机器“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)
}

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

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

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

  • 不能在 zfs.ReceiveSnapshot 的参数中使用 buff(类型 []byte)作为类型 io.Reader: []byte 没有实现 io.Reader(缺少 Read 方法)

...当我创建go install 时?

【问题讨论】:

  • 错误信息有什么不清楚的地方? []byte 没有实现 io.Reader 的 Read 方法,因此不能用作 io.Reader。请完成 Tour of Go 的方法和接口章节(从这里开始:tour.golang.org/methods/1)
  • 我知道信息很清楚,但如果我问这个,那是因为我不明白(而且我已经阅读了教程......)buff := make([]byte) 和 Read 方法 @987654327 之间的区别@

标签: go io byte reader


【解决方案1】:

当您认为 []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。

【讨论】:

  • 非常感谢! @eugenioy
  • 我不断回复这个超级有用的答案,非常感谢@eugenjoy。
【解决方案2】:

简答:使用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 参数是目标缓冲区,而不是源缓冲区。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-02
    • 2021-07-01
    • 2021-12-10
    • 1970-01-01
    • 1970-01-01
    • 2015-04-22
    相关资源
    最近更新 更多