Операторы в ядре действительно определены циклически? - PullRequest
0 голосов
/ 09 мая 2018

Мы можем реализовать черты в 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.

Как компилятор знает, как добавить два числа, не попадая в рекурсивную лазейку?

Ответы [ 2 ]

0 голосов
/ 09 мая 2018

Реализации Add, которые полагаются на оператор сложения +, в конце должны указывать на операции с примитивами (например, целыми числами) и арифметические операции на , эти реализованы с использованием Встроенные модули компилятора .

Более того, сами примитивы также являются встроенными компиляторами - обратите внимание, что вы не сможете найти их источники в документации std.

Суть в том, что примитивные типы на самом деле не нуждаются * в 1013 * коде, предоставляемом реализациями Add и чертами других арифметических операторов вообще - эти функциональные возможности обеспечиваются внутренними компонентами компилятора. Их реализации черт предоставляются в целях обобщений.

0 голосов
/ 09 мая 2018

Как компилятор узнает, что нужно добавить два числа, не попадая в рекурсивную лазейку?

Поскольку компилятор является компилятором, а компилятор знает, что ему не нужно Add Реализация добавить два номера.Если он делает постоянное сворачивание, он просто добавляет их.Если он генерирует код, он говорит LLVM добавить их во время выполнения.

Эти Add реализации не предназначены для того, чтобы сообщать компилятору, как добавлять числа, они должны реализовать Add для чисел, чтобыПользовательский код может добавлять числа через черту Add, как и любой пользовательский тип.Если бы этих реализаций не существовало, вы бы не смогли добавить числа в универсальных методах, потому что они не реализовали бы Add.

Другими словами: Add - это то, чтоКомпилятор использует, когда он иначе не знает, как добавлять вещи.Но он уже знает, как добавлять числа, поэтому они не нужны.Они предоставляются для согласования с другими типами.

...