在 Rust 中,是否可能对泛型进行编译时类型检查?

6
我不想检查一个类型是否具有某种特征,但我想能够区分例如结构体和整数。由于结构体和整数都可以实现相同的特征,我不知道如何区分它们。
我之所以想这样做,是因为我正在使用serde_json将通用类型转换为JSON,但我希望它只成为JSON对象(当它是一个结构体时才会发生),但它不应该转换成其他任何东西(例如JSON I64)。由于结构体和整数都可以实现Serialize特征,所以没有办法区分它们。
目前,我让程序崩溃,因为它不是可以恢复的错误,但由于我可能在编译时知道这一点,我想知道是否有任何机制可以在编译阶段确定类型。
我想知道如何通过它们的“类别”而不是它们的特征来区分类型。

Aochagavia刚刚指出,比较类型无法解决我示例中的问题。尽管如此,我希望这个问题能够说明问题。我猜没有优雅的解决方案,只能在运行时进行验证。或者也许可以用过程宏来解决,但那似乎有点过度设计。 - Daniel Fraenkel
你需要想出一种方法,让编译器检查:“我知道这个Serialize的实现总是返回Objects”。 - aochagavia
1个回答

4
即使在编译时您成功比较了类型,也无法阻止将struct序列化为Json::I64。它的Serialize实现可以是任何内容!我可以想到一些部分解决方案:

运行时检查

通过模式匹配添加一个运行时检查来查看结果是否确实是Json::Object。如果您希望这始终为真,可以将其与断言组合使用。我认为这就是您现在正在做的事情。

引入自定义特性

可以创建一个新的特性:

trait SerializeAsObject : Serialize {}

然后,您只需要为那些您确定将被序列化为对象的数据类型实现该特征。但是,没有任何防止您为i64实现特征,因此这里仍有错误的余地。

真正的解决方案:依赖类型

您可能需要支持依赖类型的类型系统,以确保数据类型的序列化始终产生给定类型的输出。据我所知,这样的类型系统非常复杂,没有广泛使用的语言支持它(您可以查看Idris了解更多信息)。

关于编译时检查的想法

虽然拥有编译时检查很棒,但编译器只能做到这么多。根据我的经验,在实际的编程中使用依赖类型并不值得麻烦。例如,在这种情况下,您需要提供数学证明,以便编译器可以理解Serialize的实现始终产生对象的序列化。

即使如此,也无法确保程序没有错误!因此,在这种情况下,我认为正确的做法是使用断言,记录您的函数将在无法将数据序列化为对象时引发 panic,并编写单元测试以确保正确调用。


你说得完全正确。因此,对该结构进行“类型检查”不是该示例的正确解决方案。模式匹配需要在运行时完成,这很不幸。很遗憾,我需要让进程因原则上可以在编译时知道的问题而崩溃。 - Daniel Fraenkel
在编译时,有许多已知的东西被丢弃了。即使在一种支持对代码进行推理的语言中,想象一下程序员向编译器解释他们的意图是多么困难。这变得非常复杂,非常快速。你基本上必须编写一个数学证明,以便编译器能够理解。 - aochagavia
我同意。在这种情况下,更加实用的运行时解决方案不应该是一个问题。 - Daniel Fraenkel
我认为 OCaml 的 GADT 属于依赖类型的真实世界应用。例如,这允许拥有一个“打印”函数,它在不使用宏或任何类似的东西的情况下被良好地类型化:它的类型只取决于作为其第一个参数提供的文本字符串。 - jthulhu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接