[译] Golang Reader 接口实现

Golang Reader 接口实现

尽管本文探讨的是如何实现 io.Reader 接口,但是作为实现接口的一般套路也是有意义的。在讨论接口实现的这个主题时,我发现多数文章所列举的示例都脱离的现实,比如去实现一个 Animal 接口。

首先,我们看下如何编写代码的数据接口才能满足实现 io.Reader 接口的条件。从 go 文档我们可以看到。

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

这看起来很简单,我们要做的就是去实现一个 Read 方法。本文我将实现一个 Stringer 数据结构,当调用 read 方法时,它将输出字符串。它看起来如下。

type Stringer struct {
    stringer string
    read bool
}

现在我们要去实现 io.Reader 接口,仅需要创建 Read 方法,接口签名是一个 slice 的 bytes 数据,返回 int 和 error 数据。

func (s Stringer) Read(p []bytes) (n int, err error) {

}

如果你之前使用过 go,你可能已经发现这段代码是无法工作的。由于它没有指针指向底层数据结构,所以我们无法跟踪它已经读取过的内容。这个问题的原因是当注入 io.ReadAll 多次调用 Read 方法时会期望在读到文件结束是收到一个错误通知。如果无法实现你的程序会耗尽内存。

我们真正想实现的是有如下函数签名的方法。

func (s *Stringer) Read(p []bytes) (n int, err error) {

}

它的问题在于,当你实现这个方法并将他作为参数传递个接收 io.Reader 类型的方法时,你会受到下面的信息。这里是完整代码的链接

Stringer does not implement io.Reader (Read method has pointer receiver)

译注:代码片段:

package main

import "io/ioutil"

type Stringer struct {
    name string
    done bool
}

func (s *Stringer) Read(p []byte) (n int, err error) {
    return 0, nil
}

func main() {
    s := Stringer{}
    ioutil.ReadAll(s)
}

解决的方法是,创建一个指针类型作为参数传入。不过它会抛出另一个错误,除非你正确实现 Reader 方法 http://play.golang.org/p/gyMcTp2ALX

译注:代码片段:

package main

import  "io/ioutil"
import "io"

type Stringer struct {
    name string
    done bool
}

func (s *Stringer) Read(p []byte) (n int, err error) {
    return 0, io.EOF
}

func main() {
    s := &Stringer{}
    ioutil.ReadAll(s)
}

现在来实现真正可以工作的 Read 方法。

func (r *Reader) Read(p []byte) (n int, err error) {
    if r.done {
            return 0, io.EOF
    }
    for i, b := range []byte(r.read) {
            p[i] = b
    }
    r.done = true
    return len(r.read), nil
}

如你所见,正因为我们可以操作底层数据结构,我们才能够标记合适 read 应该停止被调用。完整的实现类似于 http://play.golang.org/p/ejpUVOx8jR

译注:代码片段:

package main

import "io/ioutio"
import "io"
import "log"

type Reader struct {
    read string
    done bool
}

func NewReader(toRead string) *Reader {
    return &Reader{toRead, false}
}

func (r *Reader) Read (p []byte) (n int, err error) {
    if r.done {
        return 0, io.EOF
    }

    for i, b := range []byte(r.read) {
        p[i] = b
    }

    r.done = true
    return len(r.read), nil
}

func main() {
    M := NewReader ("test")
    stuff, _ := ioutil.ReadAll(M)
    log.Printf("%s", stuff)
}

原文: Golang’s Reader Interface

发表评论

电子邮件地址不会被公开。 必填项已用*标注