核心中的运算符真的是循环定义的吗?
Are operators in core really defined circularly?
我们可以实现 core::ops
中的特征来为我们的类型定义运算符的行为。特征本身用 #[lang =...]
属性注释,因此编译器知道哪些特征和运算符属于一起。
例如,原始类型的 Add
实现如下所示(从 here 手动扩展和简化的宏):
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
。
编译器如何知道如何将两个数字相加而不落入递归漏洞?
How does the compiler know to add two numbers without falling into a recursive loophole?
因为编译器就是编译器,编译器知道它不需要 Add
实现来添加两个数字。如果它不断折叠,它只是添加它们。如果它正在生成代码,它会告诉 LLVM 在运行时添加它们。
那些 Add
实现不是告诉编译器如何添加数字,它们是为数字实现 Add
以便用户代码可以通过 [=10= 添加数字] trait 就像任何用户定义的类型一样。如果这些实现不存在,那么您将无法在泛型方法中添加数字,因为它们不会实现 Add
.
换句话说:Add
是编译器在不知道如何添加内容时使用的内容。但它已经知道如何添加数字,所以它不需要它们。提供它们是为了与其他类型保持一致。
Add
依赖加法运算符+
的实现最终需要指向对基元(例如整数)的运算和对的算术运算[= =23=] 使用 compiler built-ins.
实现
此外,原语本身也是编译器内置的 - 请注意,您无法在 std
文档中找到它们的来源。
底线是原始类型实际上 不需要 Add
的实现提供的代码和其他算术运算符的特征 - 这些功能由编译器的内在函数提供。它们的特征实现是为了泛型的目的而提供的。
我们可以实现 core::ops
中的特征来为我们的类型定义运算符的行为。特征本身用 #[lang =...]
属性注释,因此编译器知道哪些特征和运算符属于一起。
例如,原始类型的 Add
实现如下所示(从 here 手动扩展和简化的宏):
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
。
编译器如何知道如何将两个数字相加而不落入递归漏洞?
How does the compiler know to add two numbers without falling into a recursive loophole?
因为编译器就是编译器,编译器知道它不需要 Add
实现来添加两个数字。如果它不断折叠,它只是添加它们。如果它正在生成代码,它会告诉 LLVM 在运行时添加它们。
那些 Add
实现不是告诉编译器如何添加数字,它们是为数字实现 Add
以便用户代码可以通过 [=10= 添加数字] trait 就像任何用户定义的类型一样。如果这些实现不存在,那么您将无法在泛型方法中添加数字,因为它们不会实现 Add
.
换句话说:Add
是编译器在不知道如何添加内容时使用的内容。但它已经知道如何添加数字,所以它不需要它们。提供它们是为了与其他类型保持一致。
Add
依赖加法运算符+
的实现最终需要指向对基元(例如整数)的运算和对的算术运算[= =23=] 使用 compiler built-ins.
此外,原语本身也是编译器内置的 - 请注意,您无法在 std
文档中找到它们的来源。
底线是原始类型实际上 不需要 Add
的实现提供的代码和其他算术运算符的特征 - 这些功能由编译器的内在函数提供。它们的特征实现是为了泛型的目的而提供的。