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