在示例中,我们有:
// The flag package may call the String method with a zero-valued receiver,
// such as a nil pointer.
type Value interface {
String() string
Set(string) error
}
如果用空指针接收器调用它,会导致恐慌。实际上,这段代码应该是没问题的,因为String永远不会用空指针接收器调用(flag.isZeroValue使用reflect.New,如果接收器是指针的话),但这仍然是矛盾的。
事实上,从当前的措辞来看,你可以认为这样做是可以的,但实际上并非如此:
func (x *mytype) String() string {
if x == nil { // guard for flag.Value requirement
return "foo"
}
return *x.ptr // my constructor guarantees this is fine
}
此外,从flag.Value描述中也不太清楚为什么调用者需要准备好处理他从未产生过的值。
是的,flag.PrintDefaults描述回答了这个问题,但也许在flag.Value描述中也提到一下,零值字符串表示仅用于关闭显示默认值(无论是因为它是未使用的值(意味着off)还是因为它是一个明显默认)。
例如,如果有人编写了一个flag.Value实现,其零值(不是空指针)表示一个不一致的状态(就像我所做的那样),他将不得不弄清楚他的String方法在这种情况下应该返回什么以及为什么这很重要。
8条答案
按热度按时间w8biq8rn1#
这样做是可以的,但实际上是不必要的。为什么?因为在实践中,空检查是多余的吗?
/cc @robpike
4nkexdtk2#
为什么它不好?因为在实践中,空检查是多余的吗?
还是因为它会在*x.ptr解引用时引发恐慌。
如果你的flag.Value是指针,那么flag包会使用reflect.New来生成一个新的,并对其调用String方法。因此,接收者本身不是零值,但它指向的值是。
bksxznpy3#
如果这里有什么要做的,我不确定是否有,那就是调整示例以更加谨慎:
atmip9wb4#
我愿意被证明是错的,但我认为它比那更值得一些解释。
我在这儿指出了两个问题,我认为它们都很重要:
flag.isZeroValue
调用时)。文档说接收者本身可以为零值,而不是它指向的值。这一点特别通过“例如,一个空指针”的部分得到了加强。我得出的结论是,String实现可以检查一个nil接收器,否则就假设接收者是用自己的构造函数创建的。显然你不同意这个观点,否则你就会明白其中的道理。你能解释一下为什么吗?
这是我提出的修复方案:
这就是实际发生的情况。
String从未被用nil指针调用过,虽然我不反对如果文档说它可能做比实际上更多的事情,只要它不比实际上说的多就行!
oprakyz75#
无论如何,似乎flag包只使用客户端传递的值。
fcwjkofz6#
Assigning to @robpike to fix one way or another.
kokeuurv7#
在任何情况下,似乎标志包只使用客户传递的值。
@rsc 我不确定你的意思。
flag.isZeroValue
函数从头开始使用 reflect 创建新的值,并对它们调用 String。否则,我们一开始就不需要在文档中为 String 方法设置约束。
你对我在第三个代码块中的示例有问题吗?
https://play.golang.org/p/-VO6paFNd6H
我可以给你更现实(也更复杂)的例子。
(其他未由客户端传递的值包括标志包自己的私有类型,这些类型用于所有非自定义标志,但它们可以正常工作。)
nhn9ugyo8#
See also #28667 .