问题 核心运营商是否真正循环定义?


我们可以实现这些特征 core::ops 定义我们类型的运算符的行为。特征本身用注释 #[lang =...] 属性,因此编译器知道哪些特征和运算符属于一起。

例如, Add 原始类型的实现看起来像这样(宏从手动扩展和简化 这里):

impl Add for i32 {
    type Output = i32;

    fn add(self, other: i32) -> i32 {
        self + other
    }
}

令我惊讶的是,实现使用了 + 内部运营商,可能是电话 self.add(other),导致无限递归。显然,事情并不像这样发生,因为表达式就像 3 + 4 (假设没有不断的折叠)工作完全正常。

现在考虑一下这个天真的实现 Add 特征:

use std::ops::Add;

struct Foo;

impl Add for Foo {
    type Output = Foo;

    fn add(self, other: Foo) -> Foo {
        self + other
    }
}

fn main() {
    let two_foo = Foo + Foo;
}

编译器警告说 function cannot return without recurring 并在调试模式下运行此程序正确停止 fatal runtime error: stack overflow

编译器如何知道如何添加两个数字而不会陷入递归漏洞?


7184
2018-05-09 13:13


起源

可能相关: Rust的布尔值和其他原始类型在哪里实现?。 - ljedrz
@ljedrz比相关。虽然问题完全不同,但答案非常匹配。我现在不确定这是否重复,因为这里的答案会添加与您的问题不符的信息。 - kazemakase
我认为这个问题本身很有趣;在这种情况下的来源 是 可以在 std docs及其与内在函数的联系并不明显。 - ljedrz


答案:


编译器如何知道添加两个数字而不会陷入递归漏洞?

因为编译器是编译器,并且编译器知道它不需要 Add 实现添加两个数字。如果它正在进行恒定折叠,它只是添加它们。如果它正在生成代码,它会告诉LLVM在运行时添加它们。

那些 Add 实现不是告诉编译器如何添加数字,它们是要实现的 Add 对于数字,以便用户代码可以通过 Add trait就像任何用户定义的类型一样。如果这些实现不存在,那么您将无法在泛型方法中添加数字,因为它们无法实现 Add

换一种方式: Add 是编译器在不知道如何添加内容时使用的内容。但它已经知道如何添加数字,所以它不需要它们。提供它们与其他类型的一致性。


7
2018-05-09 13:57





的实现 Add 依赖于加法运算符 + 最后需要指向对基元(例如整数)和算术运算的操作 那些 使用。实现 编译器内置函数

更重要的是,原语本身也是编译器内置函数 - 请注意,您将无法在其中找到它们的源代码 std 文档。

底线是原始类型实际上并不存在 需要 由。的实现提供的代码 Add 和其他算术运算符的特征 - 这些功能由编译器的内在函数提供。他们的特征实现是为了泛型的目的。


5
2018-05-09 13:59