【问题标题】:Could adding variadic parameters to a function break existing code?向函数添加可变参数会破坏现有代码吗?
【发布时间】:2019-03-14 12:49:50
【问题描述】:

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

例如:

// Old function
func Foo(a int)

// Updated to:
func Foo(a int, params ...string)

API 的调用者可以省略新参数,所以我认为 API 是向后兼容的。

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

【问题讨论】:

  • 鉴于 foo 未导出:没人在乎。它将被导出:这是一个制动变化。这对 semver 意味着什么可以由 semverionistas 判断。
  • 我已经删除了对 semver 的引用,因为那部分是主观的。现在的问题集中在这是否会对某人造成破坏。我还把foo 变成了Foo,因为只讨论导出的函数才有意义。

标签: go parameters variadic-functions backwards-compatibility


【解决方案1】:

我。改变功能

调用它们将继续工作而无需修改,但由于函数签名不匹配,这可能很容易破坏一些代码。

例如(在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
}

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

不能在赋值中使用 Foo2 (type 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)
}

调用process(foo) 成功,调用process(foo2) 会在运行时panic。在Go Playground 上试试吧。

二。改变方法

您的问题是针对函数的,但是方法也存在同样的“问题”(当用作method expressionsmethod values 时,例如参见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
}

因为签名不匹配,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)

【讨论】:

  • 既然他在谈论 api 的调用者,我会说这对 api 用户来说不是向后不兼容的变化,因为调用 api 的旧方式仍然有效
  • @Pizzalord 在某些情况下它可能会继续工作,但在其他情况下会中断。答案中的例子证明了这一点。这取决于它的使用方式,但我的答案中的所有用例都是有效的。
  • 大多数更改(即使是 Go 接受的那些,根据其向后兼容的承诺)都有可能破坏 something。向结构添加字段可能会破坏在不同类型的结构之间进行复制的能力,但例如使用相同的字段,但这通常由 Go 完成。因此,这种损坏对于 semver 是否重要,IMO 主要是见仁见智。
  • @Flimzy 我同意,有点正确。但是 Go 的接口设计很特别,也很强大,如果这样改变的函数是一个方法,那就会破坏隐式接口实现——在我看来这是一个严重的变化。使用新字段扩展结构并不意味着更改签名。
  • 感谢您的回答。我已经稍微简化了我的问题,因为它让人们对 semver 的歧义感到有点焦虑。但是,您仍然回答了新问题 - 有人可能会使用我的旧 API 并被更改绊倒。所以这是一个突破性的变化,我会这样处理。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-04
  • 2019-02-27
  • 2013-03-08
相关资源
最近更新 更多