GoLang - 将nil传递给接受接口的函数

3

最近我在golang中遇到了一些困惑的事情。

package main

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

func createRequest(method, uri string, data *bytes.Buffer) string {
    req, err := http.NewRequest(method, uri, data)
    if err != nil {
       return ""
    }
    req.Close = true
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
       return ""
    }
    defer resp.Body.Close()
    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return ""
    }
    return string(respBody)
}

func createRequestNoBody(method, uri string) string {
    req, err := http.NewRequest(method, uri, nil)
    if err != nil {
       return ""
    }
    req.Close = true
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
       return ""
    }
    defer resp.Body.Close()
    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return ""
    }
    return string(respBody)
}

func main() {
   data := createRequestNoBody("GET", "http://google.com")
   if len(data) != 0 {
      fmt.Println("First Request Ok")
   }
   data = createRequest("GET", "https://google.com", nil)
   if len(data) != 0 {
      fmt.Println("Second Request OK")
   }
}

当直接将nil传递给http.NewRequest的时候,这段代码可以在createRequestNoBody函数中正常工作,但是在调用函数中将*bytes.Buffer设置为nil时,调用createRequest会导致恐慌。

http库中的NewRequest函数具有以下原型:

func NewRequest(method, urlStr string, body io.Reader) (*Request, error)

其中io.Reader是一个接口。

我不确定为什么通过变量传递的nil会导致NewRequest中的body变成非nil。

有人能解释一下为什么createRequest方法会导致恐慌吗?在NewRequest方法内部,data = createRequest("GET", "https://google.com", nil)中的nil参数是如何变成非nil的呢?


4
可能是 Hiding nil values, understanding why golang fails here 的重复内容。 - icza
谢谢你的链接,@JimB。确实解释得很清楚。当它在createRequest函数之间传递时,它在接口中增加了一个类型,并将其作为(*bytes.Buffer,nil)传递到下一个函数中,而不是(nil,nil)。在调用NewRequest之前,data的值是(nil, nil)还是(*bytes.Buffer,nil)?我不确定什么时候它变成了(*bytes.Buffer,nil),这就是问题所在。 - soluwalana
2
datanil,但类型为 *bytes.Buffer - JimB
1个回答

0

@JimB在他的评论中是正确的。下面这段代码片段显示对象的类型确实为(*bytes.Buffer, nil)

package main

import (
    "bytes"
    "fmt"
    "reflect"
    "errors"
    "io"
)
func NewRequest(method, urlStr string, body io.Reader) (error) {
    if body == nil {
       fmt.Println("Body Is Nil")
       return nil
    }
    fmt.Println(reflect.TypeOf(body).Elem())
    return errors.New("NOT NIL")
}

func createRequest(method, uri string, data *bytes.Buffer) string {
    fmt.Println("Type Of data is ", reflect.TypeOf(data).Elem())

    if data == nil {
       fmt.Println("data is nil")
    }
    err := NewRequest(method, uri, data)
    if err != nil {
       return err.Error()
    }
    return "OK"
}

func createRequestNoBody(method, uri string) string {
    err := NewRequest(method, uri, nil)
    if err != nil {
       return err.Error()
    }
    return "OK"
}

func main() {
   data := createRequestNoBody("GET", "http://google.com")
   if len(data) != 0 {
      fmt.Println("First Request Ok")
   }
   data = createRequest("GET", "https://google.com", nil)
   if len(data) != 0 {
      fmt.Println("Second Request Not OK")
   }
}

运行的输出看起来像:

Body Is Nil
First Request Ok
Type Of data is  bytes.Buffer
data is nil
bytes.Buffer
Second Request Not OK

由于 body io.Reader 是一个接口而不是指向特定类型的指针,当您传入一个类型为 bytes.Buffernil 指针时,它将成为非 nil 接口。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接