Golang 类型系统不一致(http 包)

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

Golang Type system inconsistency (http package)

问题

我正在努力理解Go语言的类型系统,有几个问题让我感到困惑。

所以我一直在使用http库进行工作,以便理解其中的问题,我发现以下内容毫无意义。

package main

import (
	"net/http"
	"fmt"
	"io/ioutil"
	"io"
)

func convert(closer io.Closer) ([]byte) {
	body, _ := ioutil.ReadAll(closer);
	return body
}

func main() {

	client := &http.Client{}
	req, _ := http.NewRequest("GET", "https://www.google.com", nil)

	response, _ := client.Do(req);

	body, _ := ioutil.ReadAll(response.Body)

	fmt.Println(body);
	fmt.Println(convert(response.Body))

}

Go Playground

这不是关于不需要convert函数的事实,而是响应体的类型是io.Closer,而ioutil.ReadAll需要一个io.Reader类型的参数,然而我可以在一个实例中传递它,但在另一个实例中却不能。我是否遗漏了某些神奇的事情?

我知道closer在技术上通过实现Read方法来传递reader接口,但这在函数和主体中都应该是正确的。

如果有任何见解,将不胜感激。

谢谢。

英文:

I am trying to wrap my head around the GoLang type system, and there area a few things the confuse me.

So I have been working on the http library to try to understand this and I have come across the following that makes no sense.

package main

import (
	"net/http"
	"fmt"
	"io/ioutil"
	"io"
)

func convert(closer io.Closer) ([]byte) {
	body, _ := ioutil.ReadAll(closer);
	return body
}

func main() {

	client := &http.Client{}
	req, _ := http.NewRequest("GET", "https://www.google.com", nil)

	response, _ := client.Do(req);

	body, _ := ioutil.ReadAll(response.Body)

	fmt.Println(body);
	fmt.Println(convert(response.Body))

}

The Go Playground

this is not about the fact that the convert function is not needed it is the fact that the response body is of type io.closer and the ioutil.Readall takes a io.reader, yet I can pass it in, in one instance but not if in another. Is there something that I am missing that is magically happening.

I know that the closer technically passes the reader interface as it implements the the Read method but that should be true in both the function and the main body.

Any insight would be great.

Thanks

答案1

得分: 2

不,事实并非如此。Request.Body的声明在http.Request中:

Body io.ReadCloser

Request.Body字段的类型是io.ReadCloser,它既是io.Reader又是io.Closer

由于它是一个io.ReaderRequest.Body的动态值实现了io.Reader),你可以在需要io.Reader的地方使用/传递它,例如ioutil.ReadAll()

由于它还实现了io.Closer,你也可以在需要io.Closer的地方传递它,比如你的convert()函数。

但是在convert函数内部,closer参数的静态类型是io.Closer,你不能在需要io.Reader的地方使用closer。它可能(在你的情况下是这样)存储的动态类型也实现了io.Reader,但是不能保证这一点。就像这个例子中一样:

type mycloser int

func (mycloser) Close() error { return nil }

func main() {
    var m io.Closer = mycloser(0)
    convert(m)
}

在上面的例子中,convert()内部的closer将持有类型为mycloser的值,它确实没有实现io.Reader

如果你的convert()函数也希望将其参数视为io.Reader,那么参数类型应该是io.ReadCloser

func convert(rc io.ReadCloser) ([]byte, error) {
    body, err := ioutil.ReadAll(rc)
    if err != nil {
        return body, err
    }
    err = rc.Close()
    return body, err
}
英文:

> it is the fact that the response body is of type io.closer

No, it is not. Declaration of Request.Body is at http.Request:

Body io.ReadCloser

The Request.Body field is of type io.ReadCloser, it is both an io.Reader and an io.Closer.

Since it is an io.Reader (dynamic value of Request.Body implements io.Reader), you may use / pass it where an io.Reader is required, e.g. to ioutil.ReadAll().

Since it also implements io.Closer, you can also pass it where io.Closer is required, like your convert() function.

But inside convert the closer param has static type io.Closer, you can't use closer where an in.Reader is required. It might be (and in your case it is) that the dynamic type stored in closer also implements io.Reader, but there is no guarantee for this. Like in this example:

type mycloser int

func (mycloser) Close() error { return nil }

func main() {
    var m io.Closer = mycloser(0)
    convert(m)
}

In the above example closer inside convert() will hold a value of type mycloser, which truly does not implement io.Reader.

If your convert() function intends to treat its parameter also as an io.Reader, the parameter type should be io.ReadCloser:

func convert(rc io.ReadCloser) ([]byte, error) {
    body, err := ioutil.ReadAll(rc)
    if err != nil {
        return body, err
    }
    err = rc.Close()
    return body, err
}

huangapple
  • 本文由 发表于 2017年7月20日 20:32:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/45214860.html
匿名

发表评论

匿名网友

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

确定