Are operators in core really defined circularly?

别说谁变了你拦得住时间么 提交于 2020-05-12 11:27:40

问题


We can implement the traits in core::ops to define the behavior of operators for our types. The traits themselves are annotated with #[lang =...] attributes so the compiler knows which traits and operators belong together.

For example, the Add implementation for primitive types looks like this (macro manually expanded and simplified from here):

impl Add for i32 {
    type Output = i32;

    fn add(self, other: i32) -> i32 {
        self + other
    }
}

To my surprise, the implementation uses the + operator internally, which presumably calls self.add(other), resulting in an infinite recursion. Obviously, things do not happen like this because expressions like 3 + 4 (assuming no constant folding) work perfectly fine.

Now consider this naive implementation of the Add trait:

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;
}

The compiler warns that function cannot return without recurring and running this program in Debug mode properly stops with fatal runtime error: stack overflow.

How does the compiler know how to add two numbers without falling into a recursive loophole?


回答1:


How does the compiler know to add two numbers without falling into a recursive loophole?

Because the compiler is the compiler, and the compiler knows it doesn't need an Add implementation to add two numbers. If it's doing constant folding, it just adds them. If it's generating code, it tells LLVM to add them at runtime.

Those Add implementations aren't there to tell the compiler how to add numbers, they're to implement Add for numbers so that user code can add numbers via the Add trait just like any user-defined type. If those implementations didn't exist, then you wouldn't be able to add numbers in generic methods, because they wouldn't implement Add.

To put it another way: Add is what the compiler uses when it doesn't otherwise know how to add things. But it already knows how to add numbers, so it doesn't need them. They're provided for consistency with other types.




回答2:


The implementations of Add that rely on the addition operator + in the end need to point to operations on primitives (eg. integers) and arithmetic operations on those are implemented using compiler built-ins.

What is more, the primitives themselves are compiler built-ins as well - note that you won't be able to find their sources in the std documentation.

The bottom line is that primitive types don't actually need the code provided by the implementations of Add and other artithmetic operators' traits at all - these functionalities are provided by the compiler's intrinsics. Their trait implementations are provided for the purposes of generics.



来源:https://stackoverflow.com/questions/50254315/are-operators-in-core-really-defined-circularly

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!