4 ms·
I'm not a Rust developer, but my understanding is that if you break semver, which is very easy to do by mistake, you're in the same situation as in C++ with ODR
by mgaunard 3y ago
I'm not a Rust developer, but my understanding is that if you break semver, which is very easy to do by mistake, you're in the same situation as in C++ with ODR issues, which are undefined behaviour.
For a language that touts itself as a safer C++, that's definitely bad.
- lifthrasiir 3y agoSemantic versioning of Rust crates is just a convention and doesn't affect the correctness. Compiler (rustc) in particular has no notion of crates at all and will error whenever users try to use whatever API incorrectly.
- mgaunard 3y agoIt affects what gets rebuilt.
- lifthrasiir 3y agoThat's not even true for C/C++. A patch version can still update header files and C/C++ build systems can't detect whether the update is semantic or not. In Rust the serialized crate metadata can be used to detect if the recompilation is needed, and it is completely orthogonal to the semantic versioning.
- steveklabnik 3y ago(Rustc does know what crates are. You pass a crate root to rustc. You pass —extern to point at other crates. Rustc has no notion of packages.)
- tialaramex 3y agoC++ ODR is not just "Undefined Behaviour" it's an example of IFNDR, a get-out in which the language standard just washes its hands of difficult problems by saying in that case what you've written is silently not a C++ program, so its behaviour is arbitrary but you don't get any compiler diagnostics. Rust doesn't have this get-out. To some extent that's because it's not a formal standard, but mostly it's because IFNDR is diametrically opposed to correctness. Many C++ programs (perhaps even the vast majority) technically have no meaning whatsoever due to IFNDR which is bad -- it's not that the C++ compiler judges them to be correct when they aren't, but that it has no opinion either way, ever and programmers mistake "it compiled" for success. Thus, in Rust you don't get "ODR errors" here, your program won't compile. Here's a nice easy Rust semver break example: I make a library about US vacation booking, it has a public enumerated type FederalHoliday but initially that enum doesn't mention Juneteenth - whoops. Somebody points that out to me, and I correct the omission, but I forgot it's in a public enum. So that's a semver break. Why? If somebody writes code using FederalHoliday from my library, they're entitled to exhaustively match against the enumerated type. So they can make code to handle MLK Day and Thanksgiving and so on, and of course they had no reason to handle Juneteenth because it wasn't in the list - then one day they take my update. Their program no longer builds, it complains that their FederalHoliday code isn't handling Juneteenth, I made a semver breaking change. Now, in this type of case the correct fix (which you need to do before release, as introducing it later is a semver break) is to mark the enumeration as #[non_exhaustive] which tells Rust I know what's in this enumeration, so I can use it as an exhaustive match in my own code, but outside this crate, you can't match it exhaustively, you need to write a default case to handle new additions.
- mgaunard 3y agoThat only works if you're dealing with source, where it's possible to check for coherence. The problem happens when you link inconsistent binaries.
- lifthrasiir 3y agoIn which case static or dynamic linker will simply fail because Rust symbols have hashes derived from ABI attached by default (unless `#[no_mangle]` is used and you're on your own) and those hashes won't match unless the identical signature was used.
- mgaunard 3y agoMangling isn't magic. You have a type T, and a struct U which contains a T. A function takes a U by reference, and therefore will be mangled with U. However say that function was built with a different version of T than the caller. This doesn't affect mangling. Things silently work even though they break. This is a basic pitfall of working with any system that allows linking of binaries. The setup only works so long as you guarantee that all entities with the same name are indeed the same thing. This is the One Definition Rule.
- lifthrasiir 3y agoThose hashes are exactly designed in the way that any such changes will alter hashes and thus stop linkage. Virtually all C++ name mangling schemes don't even consider such cases: `void foo(T&);` will probably result in the same mangled name even though T's layout or any type referenced by T has been changed. In Rust they do affect mangling.
- School-Cotton 3y ago> it's an example of IFNDR, a get-out in which the language standard just washes its hands of difficult problems by saying in that case what you've written is silently not a C++ program, so its behaviour is arbitrary but you don't get any compiler diagnostics How does this differ from undefined behavior?