### 7.5.1.  警告：一個包含nil指針的接口不是nil接口

一個不包含任何值的nil接口值和一個剛好包含nil指針的接口值是不同的。這個細微區别産生了一個容易絆倒每個Go程序員的陷阱。

思考下面的程序。當debug變量設置爲true時，main函數會將f函數的輸出收集到一個bytes.Buffer類型中。

```go
const debug = true

func main() {
    var buf *bytes.Buffer
    if debug {
        buf = new(bytes.Buffer) // enable collection of output
    }
    f(buf) // NOTE: subtly incorrect!
    if debug {
        // ...use buf...
    }
}

// If out is non-nil, output will be written to it.
func f(out io.Writer) {
    // ...do something...
    if out != nil {
        out.Write([]byte("done!\n"))
    }
}
```

我們可能會預計當把變量debug設置爲false時可以禁止對輸出的收集，但是實際上在out.Write方法調用時程序發生了panic：

```go
if out != nil {
    out.Write([]byte("done!\n")) // panic: nil pointer dereference
}
```

當main函數調用函數f時，它給f函數的out參數賦了一個\*bytes.Buffer的空指針，所以out的動態值是nil。然而，它的動態類型是\*bytes.Buffer，意思就是out變量是一個包含空指針值的非空接口（如圖7.5），所以防禦性檢査out!=nil的結果依然是true。

![](../images/ch7-05.png)

動態分配機製依然決定(\*bytes.Buffer).Write的方法會被調用，但是這次的接收者的值是nil。對於一些如\*os.File的類型，nil是一個有效的接收者(§6.2.1)，但是\*bytes.Buffer類型不在這些類型中。這個方法會被調用，但是當它嚐試去獲取緩衝區時會發生panic。

問題在於盡管一個nil的\*bytes.Buffer指針有實現這個接口的方法，它也不滿足這個接口具體的行爲上的要求。特别是這個調用違反了(\*bytes.Buffer).Write方法的接收者非空的隱含先覺條件，所以將nil指針賦給這個接口是錯誤的。解決方案就是將main函數中的變量buf的類型改爲io.Writer，因此可以避免一開始就將一個不完全的值賦值給這個接口：

```go
var buf io.Writer
if debug {
    buf = new(bytes.Buffer) // enable collection of output
}
f(buf) // OK
```

現在我們已經把接口值的技巧都講完了，讓我們來看更多的一些在Go標準庫中的重要接口類型。在下面的三章中，我們會看到接口類型是怎樣用在排序，web服務，錯誤處理中的。
