【问题标题】:Abstract data type constructor can be accidentally bypassed?抽象数据类型构造函数会被意外绕过吗?
【发布时间】:2014-09-01 04:31:21
【问题描述】:

我正在尝试创建一个表示正数的抽象数据类型:

package m

type positiveNum int

func MakePositiveNum(i int) positiveNum {
    if i < 1 { panic("non positive number") }
    return positiveNum(i)
}

// some function that expects a positive number
func UsePositiveNum(s positiveNum) {}

以下是一些示例用法:

package main
import "m"
func main() {
    pn := m.MakePositiveNum(123)
    //i := 1; m.UsePositiveNum(i) // fails as expected because
                                  // int is passed instead of positiveNum
    //useInt(pn) // fails because trying to pass positiveNum instead of int
    //pn = m.positiveNum(0) // fails as expected because the type is private
    m.UsePositiveNum(pn)
}

func UseInt(int) {}

如果将m.UsePositiveNum(pn) 替换为m.UsePositiveNum(0),它仍然可以编译,绕过正数类型检查。为什么?

【问题讨论】:

  • 因为int .. 的值为零,因此positiveNum .. 为0。
  • 如果人们通过文字创建你的类型是不安全的,那么就不要导出你的类型,只导出它满足的接口; compress/bzip2 通过返回 io.Reader 来执行类似的操作。否则,Go 风格对公开数据是开放的,不像 C++ 等;像http.Server 这样的标准库类型可以做到这一点。如您所见,试图在 Go 中遵循 C++ 规则并不顺利。 Go 实际上是一种 styleset of conventions,而不仅仅是一种语言,所以如果你想学习它,那就学习整个东西。
  • 它没有被导出。第 3 行:type positiveNum int。小写表示不导出,对吧?
  • 啊,注意,我误解了你的问题,它比我想象的更有趣(詹姆斯和彼得的回答实际上回应了你的问题,不像我的评论)。通常来说,将您的类型与不是通过您的函数的访问隔离开来的一种方法是遵循compress/bzip2 的示例并返回一个接口而不是您在内部表示类型的任何内容。 peterSO 的方式看起来也不错。

标签: go abstract-data-type


【解决方案1】:

这里发生的是0untyped constantthis rule about assignability 涵盖了此类常量:

x 可分配给T 类型的变量(“x 可分配给T”)在任何这些情况下:

  • ...
  • x 是一个无类型常量,可以用 T 类型的值表示。

由于positiveNum的底层类型是int,可以表示0,所以转换不会出错。

@peterSO 的回答提供了一种避免这种隐式转换的方法,因为没有从整数常量到结构的隐式转换。请注意,它不能防止恶意用户创建像 positive.Positive{0} 这样的值,但这通常不是问题。

【讨论】:

    【解决方案2】:

    当然可以编译。没有什么可以阻止 positiveNum 类型的值为零或更小。您拥有的唯一运行时检查位于MakePositiveNum,当您执行以下操作时您永远不会调用它:

    m.UsePositiveNum(0)
    

    每个接收positiveNum 类型值的函数/方法都必须进行验证,如果你想确定的话,不仅仅是MakePositiveNum。否则,您必须假设开发人员将始终使用MakePositiveNum 来创造价值。

    您可以使用image.Rectangle 在标准库中找到类似的内容。它的许多方法假设 Min.X

    矩形的方法总是为格式正确的输入返回格式正确的输出。

    【讨论】:

      【解决方案3】:

      您可能正在寻找这样的东西:

      ADT:

      package positive
      
      type Positive struct{ i uint64 }
      
      func New(i int) Positive {
          if i < 1 {
              panic("not a positive number")
          }
          return Positive{i: uint64(i)}
      }
      
      func Function(p Positive) Positive { return p }
      
      func (p Positive) Method() Positive { return p }
      
      func (p Positive) Integer() uint64 { return p.i }
      

      用法:

      package main
      
      import "positive"
      
      func main() {
          pn := positive.New(123)
          i := 1; positive.Function(i) // fails
          UseInt(pn) // fails
          pn = positive.Positive(0) // fails
          positive.Function(pn)
          positive.Function(0) // fails
      }
      
      func UseInt(int) {}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-24
        • 1970-01-01
        • 2010-09-20
        • 1970-01-01
        • 2016-08-21
        • 1970-01-01
        • 1970-01-01
        • 2023-03-04
        相关资源
        最近更新 更多