无法将 (type []byte) 用作类型 io.Reader。

huangapple go评论109阅读模式
英文:

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

问题

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

package main

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

func main() {
	// 监听传入的连接。
	l, err := net.Listen("tcp", "192.168.99.5:9977")
	if err != nil ...
	// 应用程序关闭时关闭监听器。
	defer l.Close()
	fmt.Println("Listening on " + CONN_HOST + ":" + CONN_PORT)
	for {
		// 监听传入的连接。
		conn, err := l.Accept()
		if err != nil ...
		
		// 在新的goroutine中处理连接。
	    go handleRequest(conn)
	}
}

// 处理传入的请求。
func handleRequest(conn net.Conn) {
	// 创建一个缓冲区来存储传入的数据。
	buff := make([]byte, 1024)
	// 将传入的连接读取到缓冲区中。
	_, 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)
	// 向联系我们的人发送响应。
	conn.Write([]byte("Received!"))
	// 使用完毕后关闭连接。
	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包的文档中看到了io.Reader的定义:

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

为什么在执行go install时会收到以下错误:

  • cannot use buff (type []byte) as type io.Reader in argument to zfs.ReceiveSnapshot:
    []byte does not implement io.Reader (missing Read method)
英文:

I don't understand the error, this is my main.go that I execute in the machine "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()
}

Now, I show you the function ReceiveSnapshot from github.com/mistifyio/go-zfs/zfs.go:

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

I have seen in golang pkg the doc of io.Reader:

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

Why do I receive the error...

  • cannot use buff (type []byte) as type io.Reader in argument to zfs.ReceiveSnapshot:
    []byte does not implement io.Reader (missing Read method)

...when I make go install ?

答案1

得分: 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

英文:

I think you are missing a step in your logic when you think that []byte would be equivalent to Reader just because the Reader's Read method receives a []byte as a parameter.

Let me try to clarify:

Your ReceiveSnapshot function expects a Reader as a parameter:

ReceiveSnapshot( input io.Reader ...

In order for a type to fulfil the Reader interface, that type should itself implement this function:

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

Note that the type should implement that function in order to be a Reader.

[]byte does not implement a Read function. It is just a coincidence that the argument to Read happens to be a []byte.

In order for this to work, you need to send ReceiveSnapshot a proper Reader.

Luckily for you, having a []byte and wanting to Read for it is a common situation so the API provides an easy way to do this:

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

You just need to send bytes.NewReader(buff) to your ReceiveSnapshot function instead of just buff.

答案2

得分: 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参数是目标缓冲区,而不是源。

英文:

Short answer: Wrap your buffer in a Reader type by using bytes.NewReader

Alternatively, you could use bytes.NewBuffer with similar effect.

If the source is a string, you can use strings.NewReader.

The list of Readers goes on and on: https://golang.org/search?q=Read#Global


Explanation of the deeper question

The deeper question being: Why doesn't an array support the io.Reader interface directly?

io.Reader supports the concept of reading from a general data stream for which the total size is not necessarily known in advance. In order to support this, Read is called repeatedly until all input data is exhausted. In many languages, similar read functions must be called at least twice, where the final call returns a flag indicating end-of-file.

By returning two values (one of which is type error), Go makes it possible for reading of arrays to complete in one-shot, but only if the destination buffer is large enough to consume all available data -- which isn't always known in advance.

The io.Reader interface specifies the signature and behavior of the Read() function:

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.

So due to the way the io.Reader interface works, a simple byte buffer is not capable of implementing it. A wrapper structure is required in order to remember the state between subsequent calls to Read().

For the sake of interest, here's an example showing how that can be implemented...

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

Notice, also, that the []byte parameter to Read() is the destination buffer, not the source.

huangapple
  • 本文由 发表于 2017年5月19日 17:11:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/44065935.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定