向函数添加可变参数会破坏现有代码吗?

Dun*_*nes 7 parameters backwards-compatibility variadic-functions go

向现有的Go函数添加可变参数是一项重大变化吗?

例如:

// Old function
func Foo(a int)

// Updated to:
func Foo(a int, params ...string)
Run Code Online (Sandbox Code Playgroud)

API的调用者可以忽略新参数,因此我认为该API向后兼容。

谁能提供一个示例,说明旧API的用户在不更改其代码的情况下无法使用新API的情况?

icz*_*cza 11

一,功能变更

调用它们将继续工作,而无需进行修改,但是由于函数签名不匹配,因此很容易破坏某些代码。

例如(在Go Playground上尝试):

func Foo(a int)                    {}
func Foo2(a int, params ...string) {}

func main() {
    var f func(int)

    f = Foo
    f = Foo2 // Compile-time error!

    _ = f
}
Run Code Online (Sandbox Code Playgroud)

该行f = Foo2产生一个编译时错误:

不能在赋值中使用Foo2(func(int,... string)类型)作为func(int)类型

因此,这是向后不兼容的更改,请不要这样做。

上面的示例给出了一个编译时错误,这是幸运的/更好的情况,但是也可能有一些代码只会在运行时失败(如果/发生,则不确定),例如此示例:

func Foo(a int)                    {}
func Foo2(a int, params ...string) {}

func main() {
    process(Foo)
    process(Foo2) // This will panic at runtime (type assertion will not hold)!
}

func process(f interface{}) {
    f.(func(int))(1)
}
Run Code Online (Sandbox Code Playgroud)

调用process(foo)成功,调用process(foo2)将在运行时出现紧急情况。在Go Playground上尝试一下。

二。改变方法

您的问题直接针对函数,但方法也存在相同的“问题”(当用作方法表达式方法值时,例如,请参见golang-pass method to function)。

此外,这可能会破坏隐式接口的实现(可能使类型不实现接口),如本例所示(在Go Playground上尝试):

type Fooer interface {
    Foo(int)
}

type fooImpl int

func (fooImpl) Foo(a int) {}

type fooImpl2 int

func (fooImpl2) Foo(a int, params ...string) {}

func main() {
    var f Fooer

    f = fooImpl(0)
    f = fooImpl2(0) // Compile time error!

    _ = f
}
Run Code Online (Sandbox Code Playgroud)

由于签名不匹配,因此fooImpl2不会实现Fooer,即使这样fooImpl做:

cannot use fooImpl2(0) (type fooImpl2) as type Fooer in assignment:
  fooImpl2 does not implement Fooer (wrong type for Foo method)
      have Foo(int, ...string)
      want Foo(int)
Run Code Online (Sandbox Code Playgroud)

  • @Pizzalord 在某些情况下可能会继续工作,但在其他情况下会中断。答案中的例子证明了这一点。这取决于它的使用方式,但我回答中的所有用例都是有效的。 (2认同)
  • @Flimzy我同意,有些正确。但是Go的界面设计非常特殊且功能强大,如果以这种方式更改函数是一种方法,那将破坏隐式接口的实现–我认为这是一个严重的更改。用新字段扩展结构并不意味着签名更改。 (2认同)