ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Go 语言中,有时 nil 并不是一个 nil

2020-08-10 19:03:00  阅读:275  来源: 互联网

标签:语言 nil Writer fred io Go sLogger


今天,我遇到了一个 Go FAQ。首先,作为一个小小的 Go 语言测验,看看您是否在 Go playground 中运行该程序之前就能推断出它应该打印出的内容(我已经将程序放在侧边栏中,以防它在 Go playground 上消失)。该程序的关键代码是:

type fake struct { io.Writer }

func fred (logger io.Writer) {
   if logger != nil {
      logger.Write([]byte("..."))
   }
}

func main() {
   var lp *fake
   fred(nil)
   fred(lp)
}

由于 Go 语言中的变量是使用它们的零值显式创建的,在指针的情况下,例如 lp 将会是 nil,您可能期待上述代码会正常运行(即不执行任何操作)。实际上,它会在对 fred() 的第二次调用时崩溃。原因是,在 Go 语言中,有时以 nil 为值的变量,如果直接打印的话,它虽然看起来像 nil,但实际上并不是真的 nil 。简而言之,Go 语言区别对待 nil 接口值和转换为接口的值为 nil 的具体类型。只有前者确实为 nil,因此与字面上的 ni l 相等,就像 fred() 在这里做的一样。

(因此,可以使用 nil f 调用 (f *fake) 上的具体方法。它也许是一个 nil 指针,但是它是类型化的 nil 指针,所以可以拥有有方法。甚至在接口转换后依然可以拥有方法,正如上述的例子。)

对于这里的情况,其解决方法是更改初始化的过程。实际的程序条件性地设置了 fake,类似于下面的代码:

var l *sLogger

if smtplog != nil {
    l = &sLogger
    l.prefix = logpref
    l.writer = bufio.NewWriterSize(smtplog, 4096)
}
convo = smtpd.NewConvo(conn, l)

这会将具体类型为 *sLoggernil 传递给期望参数为 io.Writer 的对象,从而导致接口转换并掩盖了 nil。为了解决这个问题,我们可以添加一个必须显式设置的中间变量 io.Writer

var l2 io.Writer

if smtplog != nil {
    l := &sLogger
    l.prefix = logpref
    l.writer = ....
    l2 = l
}
convo = smtpd.NewConvo(conn, l2)

如果我们不初始化这个特殊的日志记录器 sLogger,则 l2 会是一个真正的 io.Writer nil,并会在 smtpd 包中被检测到。

(您可以将类似的初始化操作封装进一个返回类型为 io.Writer 的函数中,并在没有提供日志记录器的情况下显式返回 nil,通过这样的技巧来达到类似的效果。需要强调的一点是,函数必须返回接口类型,如果返回类型为 *sLogger,那么您将再次遇到相同的问题。)

sLogger 的方法中保留对零值的防护代码,这是一个个人喜好问题。然而,我不想这么做,如果将来我在代码中遇到类似的初始化错误,我希望它崩溃,以便对其进行修复。

我从这件事中学到的另一个教训是,如果是出于调试的目的而进行的打印,我不会再使用 %v 作为格式说明符,而会使用 %#v。因为前者将会为接口 nil 和具体类型的 nil 同样打印一个普通且具有误导性的 ,而 `%#v` 将为前者打印出 ,为后者打印 (*main.fake)(nil)

边注栏: 测试程序

package main

import (
    "fmt"
    "io"
)

type fake struct {
    io.Writer
}

func fred(logger io.Writer) {
    if logger != nil {
        logger.Write([]byte("a test\n"))
    }
}

func main() {
    // 这里的 t 的值是 nil
    var t *fake

    fred(nil)
    fmt.Printf("passed 1\n")
    fred(t)
    fmt.Printf("passed 2\n")
}

via: https://utcc.utoronto.ca/~cks/space/blog/programming/GoNilNotNil

作者:ChrisSiebenmann 译者:anxk 校对:polaris1119

本文由 GCTT 原创编译,Go语言中文网 荣誉推出

标签:语言,nil,Writer,fred,io,Go,sLogger
来源: https://www.cnblogs.com/polaris1119/p/13471589.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有