Go 如何确保自定义数据结构中的编译时安全性

Go 如何确保自定义数据结构中的编译时安全性,go,interface,Go,Interface,我正在编写一些数据结构,以了解围棋语言,并努力解决围棋缺乏泛型的问题 在我的实现中,我选择强制每个用户实现一个接口,这样我的结构就可以抽象地引用这些对象,但我不喜欢我的解决方案,因为这在编译时没有得到验证,正如您将看到的那样 比较器接口 如果您只想保存原始类型,则容器中保存的每个对象都必须实现以下签名的比较函数 type Comparer interface { Compare(Comparer) int } 然后,您可以拥有实现接口的各种元素,如float64或自定义结构: 浮动64

我正在编写一些数据结构,以了解围棋语言,并努力解决围棋缺乏泛型的问题

在我的实现中,我选择强制每个用户实现一个接口,这样我的结构就可以抽象地引用这些对象,但我不喜欢我的解决方案,因为这在编译时没有得到验证,正如您将看到的那样

比较器接口 如果您只想保存原始类型,则容器中保存的每个对象都必须实现以下签名的比较函数

type Comparer interface {
    Compare(Comparer) int
}
然后,您可以拥有实现接口的各种元素,如float64或自定义结构:

浮动64 这里的问题是,我不应该能够比较一个人和一个数字。我意识到我可以在运行时检查类型,但我想找到一种编译时方法来验证类型,或者另一种方法来实现数据结构

问题

我知道,使用内置的数据结构,几乎可以完成所有可能需要的操作。。。但是,如果没有泛型或运行时类型检查,人们如何创建自己的数据结构呢? 既然Go中的接口实现似乎使用duck类型,那么Go如何在编译时强制执行类型呢?
我的意思是这个代码没有什么不安全的地方。。。编译时没有安全性。例如,在下面的方法中,第一行在比较器上执行类型断言,如果它不是一个数字,并且您在LHS上没有第二项的u,那么它将返回一个错误,您可以相应地执行操作。或者您可以在根本不使用该参数的情况下调用它,然后会出现恐慌,将其留给调用方来处理是合适的,因为他们是使用错误参数调用该方法的人,这就像在C中获得InvalidOperationException一样


这与像C这样的语言的区别纯粹在于泛型,它允许您以更高的编译时安全性来执行这类操作,因为您不能错误地调用方法。也就是说,在C语言出现泛型和许多语言之前,有一段时间它们根本就没有特性。这些操作并不比在有泛型的语言中常规执行的强制转换更不安全。

如果要防止错误的类型通过比较函数,请检查类型断言的ok值,或者删除它,让代码死机。你在写一篇允许比较不同类型的文章,然后问为什么它可以比较不同的类型。是的,我同意我可以处理错误类型的事情。我没有在我的实现中处理返回的ok值,因为它在我的测试中,我知道我用写类型调用它。我的主要问题是,我认为强制用户实现接口是一件乏味的事情,接口解决方案只能在运行时强制执行。您可以在编译时在包范围内使用var myType=interfaceTypenil之类的工具强制执行接口实现。这是相当普遍的做法。您所说的是将错误的类型传递到允许多态行为的方法中,方法是采用常规类型并向下转换到特定类型。尝试C中的等效项,它将抛出InvalidOperationException。在许多情况下,您可以使用一个通用方法来代替它,它会在编译时给您带来错误,但即使在这种情况下,您也会遇到同样的问题。出现问题的原因是比较器接口{…}只有一个方法,而该方法也将比较器作为参数。如果你重新设计你的解决方案,这个问题可能会消失。实际上,您并没有编写这样的代码,所以这里没有真正的问题。编译时安全性很好,但在调用接口方法时并没有那个么必要。我很想知道为什么会这样。如果它没有显示研究结果或没有帮助,我很乐意修复它。我知道可以添加运行时检查来处理这种情况,但我不同意执行运行时检查与编译时检查一样安全。但我的主要问题是,我正在尝试编写数据结构,并正在寻找一种标准/对用户要求较低的解决方案。我愿意继续这个解决方案,如果这是在围棋程序员的期望。“我对这个环境不熟悉。@卡彭特是的,对不起,我不是说它很安全,它不是。”。但这一切都很顺利。正如我在一篇评论中指出的,您可以在编译时验证接口实现,但这不是您在这里要求的。您是说这个方法可以接受实现这个接口的任何东西,然后想要强制执行调用类型和参数是相同的。正如我所说,泛型解决了这个问题,但是在当前版本的.NET中,可能有数千个方法不是泛型的,在所有情况下,它们的行为都与Go行为相同,因为它不具有泛型的特性。你是对的。问题不在这里
这就是我要找的,它确实让人觉得Go根本无法强制执行类型。“我会解决这个问题的。”卡彭特:不管怎样,我大体上同意你的看法。事情就是这样。Go团队似乎非常坚决地不在语言中添加泛型。我个人认为这是Go最大的缺点,但您已经习惯了编写不同风格的代码,而且它确实工作得很好。
type number float64

func (n1 number) Compare(comparer Comparer) int {
    n2, _ := comparer.(number)
    if n1 > n2 {
        return 1
    } else if n1 < n2 {
        return -1
    } else {
        return 0
    }
}
type Person struct {
    Age int
}

func (p1 Person) Compare(comparer Comparer) int {
    p2, _ := comparer.(Person)
    if p1.Age > p2.Age {
        return 1
    } else if p1.Age < p2.Age {
        return -1
    } else {
        return 0
    }
}
func main() {
    fmt.Println(number(2).Compare(number(4)))    // -1
    fmt.Println(Person{26}.Compare(Person{28}))  // -1
    fmt.Println(Person{26}.Compare(number(28)))  //  1
}
func (n1 number) Compare(comparer Comparer) int {
    n2, _ := comparer.(number)
    if n1 > n2 {
        return 1
    } else if n1 < n2 {
        return -1
    } else {
        return 0
    }
}