Does rust have ODR violations?

I was just watching this talk about the one definition rule in C++.
The speaker said that there is no way to solve this without giving up on parallel compilation.

Because Rust also has parallel compilation, but I never heard anything about ODR in the Rust book, I was wondering if Rust has the same problem? If not, why?

AFAIK, the reason is simple: all names are mangled by default, and having duplicated unmangled items is explicitly UB.

1 Like

The root of the difference is that in C++ you can have multiple source files define the same item in the same namespace. In Rust, however, every source file is a separate namespace, so it's impossible to have the same item defined in separate locations (without #[no_mangle]).

6 Likes

I think the issue goes slightly deeper than namespacing/mangling.

Rust doesn't have the parallel compilation to the same extent that C++ does. Rust compilation units form a direct acyclic graph, and have to be compiled in topological sort order. In contrast, C++ compilation model is embarrassingly parallel (meaning that you can compiled all CUs in parallel) (at least, pre C++20 modules).

Rust's analogue to ODR would be coherence rules, and, indeed the same DAG structure that prevents embarrassingly parallel compilation allows to check coherence at compiled time. This is an interesting tradeoff, thanks for pointing this out!

That said, I think the practical solution here would be to push ORD/coherence checks to the linking step .

3 Likes

The coherence checks exist to prevent introducing new trait implementations from being breaking changes. It defines exactly who is allowed to implement what trait. Without it, you could have one crate implementing a trait for a specific case and then another crate that implements that trait in a more general case in a future version.

It would technically also be possible to trait all methods in trait implementations as #[inline]. This would cause them to be codegened in every object file that uses them and be marked as local to the object file, thereby preventing any conflict.

I don't think version compatibility (temporal dimension) is the primary motivation for coherence. It seems to be spatial -- with coherence, you can link any two crates and be sure that they don't introduce two different impls. Without coherence, you can have impl Foo for S for the same Foo and S in two different crates, and linking those two crates (even indirectly) would have to be an error.

The original (pre-1.0) coherence rules weren't mainly designed for future-version-compatibility, but a significant part of the current system was specifically added to address that case:

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.