Мы можем реализовать черты в 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.
Как компилятор знает, как добавить два числа, не попадая в рекурсивную лазейку?