2014-09-01 13 views
2

我試圖使代表正數的抽象數據類型:抽象數據類型構造函數可能被意外繞過?

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(0)更換m.UsePositiveNum(pn),它仍然編譯,繞過正數number typecheck。爲什麼?

+0

因爲'int'..因此'positiveNum'..的零值是0. – 2014-09-01 04:35:56

+0

如果人們通過文字創建類型是不安全的,請不要導出您的類型,只需要一個接口滿足; 'compress/bzip2'通過返回'io.Reader'來完成這樣的事情。否則,Go風格可以公開數據,而不像C++等。像'http.Server'這樣的標準庫類型可以做到這一點。正如你所看到的,試圖在Go中遵循C++規則並不會很順利。 Go實際上是[樣式](https://code.google.com/p/go-wiki/wiki/CodeReviewComments)和[一組約定](http://golang.org/doc/effective_go.html),不只是一種語言,所以如果你想學習它,學習整個事情。 – twotwotwo 2014-09-08 18:48:30

+0

它不會被導出。第3行:'輸入positiveNum int'。小寫意味着不出口,對吧? – Dog 2014-09-08 20:34:25

回答

2

這到底是怎麼發生的是0untyped constant。這樣常數由this rule about assignability覆蓋:

x是分配給T類型的變量( 「x是分配給T」)在任何情況下:

  • ...
  • x是可由T類型的值表示的無類型常量。

由於positiveNum的底層類型爲int,其可以代表0,轉換髮生而不會出現錯誤。

@ peterSO的答案提供了一種方法來避免這種隱式轉換,因爲沒有從整型常量到結構的隱式轉換。請注意,它不會防止惡意用戶創建像positive.Positive{0}這樣的值,但這通常不是問題。

1

當然它編譯。沒有什麼能夠阻止positiveNum類型的值爲零或更少。唯一運行檢查你是MakePositiveNum,你從來沒有當你做以下稱:

m.UsePositiveNum(0) 

每個函數/方法接收positiveNum類型的值必須做驗證,不僅MakePositiveNum,如果你想要確定。否則,您必須假定開發人員將始終使用MakePositiveNum來創建該值。

你可以在標準庫中找到類似於image.Rectangle的東西。許多it's方法假定Min.X < = Max.X和Min.Y < = Max.Y,但沒有實際驗證,只有保證:

一個長方形的方法總是返回以及格式輸出的格式輸出。

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