go flag:值描述不准确,关于零值

mmvthczy  于 4个月前  发布在  Go
关注(0)|答案(8)|浏览(55)

在示例中,我们有:

// 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方法在这种情况下应该返回什么以及为什么这很重要。

w8biq8rn

w8biq8rn1#

这样做是可以的,但实际上是不必要的。为什么?因为在实践中,空检查是多余的吗?
/cc @robpike

4nkexdtk

4nkexdtk2#

为什么它不好?因为在实践中,空检查是多余的吗?
还是因为它会在*x.ptr解引用时引发恐慌。
如果你的flag.Value是指针,那么flag包会使用reflect.New来生成一个新的,并对其调用String方法。因此,接收者本身不是零值,但它指向的值是。

bksxznpy

bksxznpy3#

如果这里有什么要做的,我不确定是否有,那就是调整示例以更加谨慎:

// String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics.
func (i *interval) String() string {
    if i == nil {
        return "[]"
    }
    return fmt.Sprint(*i)
}
atmip9wb

atmip9wb4#

我愿意被证明是错的,但我认为它比那更值得一些解释。
我在这儿指出了两个问题,我认为它们都很重要:

  1. 示例中的String方法显然没有满足一个明确的要求(flag包说它可能做一些会让其恐慌的事情)。请澄清为什么这不是一个问题。
  2. 请参阅我在第三个代码块中的例子:我本以为它会正常工作,但它会恐慌(当被flag.isZeroValue调用时)。文档说接收者本身可以为零值,而不是它指向的值。这一点特别通过“例如,一个空指针”的部分得到了加强。我得出的结论是,String实现可以检查一个nil接收器,否则就假设接收者是用自己的构造函数创建的。
    显然你不同意这个观点,否则你就会明白其中的道理。你能解释一下为什么吗?
    这是我提出的修复方案:
// The flag package may call the String method with a zero-valued receiver,
// or with a pointer pointing to a zero value for the base type (in case of
// a pointer receiver).

这就是实际发生的情况。
String从未被用nil指针调用过,虽然我不反对如果文档说它可能做比实际上更多的事情,只要它不比实际上说的多就行!

oprakyz7

oprakyz75#

无论如何,似乎flag包只使用客户端传递的值。

fcwjkofz

fcwjkofz6#

Assigning to @robpike to fix one way or another.

kokeuurv

kokeuurv7#

在任何情况下,似乎标志包只使用客户传递的值。
@rsc 我不确定你的意思。
flag.isZeroValue 函数从头开始使用 reflect 创建新的值,并对它们调用 String。
否则,我们一开始就不需要在文档中为 String 方法设置约束。
你对我在第三个代码块中的示例有问题吗?
https://play.golang.org/p/-VO6paFNd6H
我可以给你更现实(也更复杂)的例子。
(其他未由客户端传递的值包括标志包自己的私有类型,这些类型用于所有非自定义标志,但它们可以正常工作。)

相关问题