抽象数据类型构造函数可能会被意外绕过?

时间:2014-09-01 04:31:22

标签: go abstract-data-type

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

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),它仍会编译,绕过正数量的类型检查。为什么呢?

3 个答案:

答案 0 :(得分:2)

这里发生的事情是0untyped constant。这些常量由this rule about assignability

涵盖
  

x可分配给T类型的变量(&#34; x可分配给T&#34;)在任何这些情况下:

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

由于positiveNum的基础类型为int,可以代表0,因此转换无错误。

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

答案 1 :(得分:1)

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

m.UsePositiveNum(0)

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

您可以使用image.Rectangle在标准库中找到类似的内容。它的许多方法都假设Min.X <= Max.X和Min.Y&lt; = Max.Y,但没有实际验证,只保证:

  

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

答案 2 :(得分:1)

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

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) {}