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