英文:
Abstract data type constructor can be accidentally bypassed?
问题
我正在尝试创建一个表示正数的抽象数据类型:
package m
type positiveNum int
func MakePositiveNum(i int) positiveNum {
if i < 1 { panic("non positive number") }
return positiveNum(i)
}
// 一些期望正数的函数
func UsePositiveNum(s positiveNum) {}
以下是一些示例用法:
package main
import "m"
func main() {
pn := m.MakePositiveNum(123)
//i := 1; m.UsePositiveNum(i) // 预期失败,因为传递的是 int 而不是 positiveNum
//useInt(pn) // 预期失败,因为尝试传递 positiveNum 而不是 int
//pn = m.positiveNum(0) // 预期失败,因为该类型是私有的
m.UsePositiveNum(pn)
}
func UseInt(int) {}
如果你将 m.UsePositiveNum(pn)
替换为 m.UsePositiveNum(0)
,它仍然可以编译通过,绕过了正数类型检查。为什么会这样呢?
英文:
I'm trying to make an abstract data type representing a positive number:
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) {}
Here are some example uses:
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) {}
If you replace m.UsePositiveNum(pn)
with m.UsePositiveNum(0)
, it still compiles, bypassing the positive number typecheck. Why?
答案1
得分: 2
这里发生的情况是,0
是一个未指定类型的常量。这种常量受到关于可赋值性的规则的约束:
一个值
x
在以下任何情况下都可以赋值给类型为T
的变量("x
可以赋值给T
"):
- ...
x
是一个可以用T
类型的值表示的未指定类型的常量。
由于 positiveNum
的底层类型是 int
,可以表示为 0
,所以转换可以顺利进行。
@peterSO 的答案提供了一种避免这种隐式转换的方法,因为从整数常量到结构体没有隐式转换。请注意,这并不能防止恶意用户创建像 positive.Positive{0}
这样的值,但通常这不是一个问题。
英文:
What is happening here is that 0
is an untyped constant. Such constants are covered by this rule about assignability:
> A value x
is assignable to a variable of type T
("x
is assignable to T
") in any of these cases:
>
> * ...
> * x
is an untyped constant representable by a value of type T
.
Since positiveNum
's underlying type is int
, which can represent 0
, the conversion happens without error.
@peterSO's answer provides a way to avoid this implicit conversion, since there is no implicit conversion from an integer constant to a struct. Note that it won't protect against a malicious user creating a value like positive.Positive{0}
, but that is usually not a concern.
答案2
得分: 1
当然可以编译。没有任何东西阻止类型为positiveNum
的值为零或更小。你唯一的运行时检查在MakePositiveNum
中,但你在执行以下操作时从未调用它:
m.UsePositiveNum(0)
每个接收positiveNum
类型值的函数/方法都必须进行验证,不仅仅是MakePositiveNum
,如果你想确保。否则,你必须假设开发者将始终使用MakePositiveNum
来创建该值。
你可以在标准库中找到类似的例子,比如image.Rectangle
。它的许多方法假设Min.X <= Max.X
和Min.Y <= Max.Y
,但实际上没有进行验证,只有以下保证:
对于合法输入,矩形的方法始终返回合法的输出。
英文:
Of course it compiles. There is nothing that prevents a value of type positiveNum
to be zero or less. The only runtime check you have is in MakePositiveNum
, which you never call when you do the following:
m.UsePositiveNum(0)
Every function/method that receives a value of type positiveNum
must do the validation, not only MakePositiveNum
if you want to be sure. Else you have to assume the developer will always use MakePositiveNum
to create the value.
You can find something similar in the standard library with image.Rectangle
. Many of it´s methods assume that Min.X <= Max.X and Min.Y <= Max.Y, but there is no actual validation, only the guarantee that:
> A rectangle's methods always return well-formed outputs for well-formed inputs.
答案3
得分: 1
你可能正在寻找类似这样的内容:
ADT(抽象数据类型):
package positive
type Positive struct{ i uint64 }
func New(i int) Positive {
if i < 1 {
panic("不是正数")
}
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) // 失败
UseInt(pn) // 失败
pn = positive.Positive(0) // 失败
positive.Function(pn)
positive.Function(0) // 失败
}
func UseInt(int) {}
英文:
You may be looking for something like this:
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 }
Usage:
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) {}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论