This is what people used to call “DLL Hell,” in Microsoft Windows.
COM was supposed to fix that, but I don’t think it worked especially well.
This is what people used to call “DLL Hell,” in Microsoft Windows.
COM was supposed to fix that, but I don’t think it worked especially well.
COM fixes having a cross language OOP ABI, that is all.
The things that were supposed to fix that were:
1- Version resources, which hardly matter, because the app loading them has to validate the versions itself
2- .NET, hence the Global Assembly Cache and having the version as part of the Assembly (DLL) lookup. Still has issues if the lookup rules were badly configured on the app.
3- Application manifests, bringing the .NET ideas to Win32
4- Registry Free COM, extension of app manifests, allowing direct lookup of desired COM libraries without going through the registry for the version
5- UWP sandbox, didn't took off, only what is inside the sandbox is searched for
6- Easiest one, don't put stuff all over the place, only search inside an application specific directory
In practice, a lot of orgs just stopped including deprecated versions.
I would assume that both versions publish the same API, so does Rust add a mangling to the link reference?
One of the things that made DLL Hell so bad, was that you would have no idea that you were calling a deprecated/new version of the function, until it started misbehaving.
What does get complicated is when the crates A and B both deal in types from X. If a function from A returns a type from X and a function from B takes that same type as an argument, then the compiler will step in with an error that the types don’t match. It’ll tell you that type Foo (from crate X version 1.0) doesn’t match Foo (from crate X version 2.0). This prevents all the possible runtime errors that could occur if you were really mixing an matching between both version. In that situation you will likely need to constrain your chosen versions of A and/or B such that they can agree on a single version of X, instead of allowing cargo to simply pick the latest available version.
Or you could use a safe dynamic language like Lisp, Javascript, Python, and many others. Programmers using these languages are so productive because they never have to put up with any nonsense like random heap corruption.
Or you could use Rust, which gives you the safety without any of the runtime costs incurred by the dynamic languages. What you call Byzantine complexity is in fact a very simple rule that you can teach to anybody, and which in practice most developers never need to deal with. It doesn’t even slow down the compiler.
Engineering is all about tradeoffs, so choose wisely.
I keep running into this with the Rust 3D graphics stack. That's winit (windowing), wgpu (wrapper for Vulkan, Metal, WebGPU, etc.), egui (2D menu overlay), and support crates such as wgpu-egui, wgpu-profiling, tracy-profiler, and glam (vector and matrix math). Every time one of these has a breaking change, it takes 4 to 6 weeks before the whole stack works without patching. Then I have to fix three more levels of my own.
But at least if you end up with incompatible versions then the compiler will step in and prevent your program from compiling, instead of allowing it to crash mysteriously later on. It’s frustrating, but not half so frustrating as it could be.
A big advantage of that is that you seldom have to do a clean build in Rust. Make-based systems seem to require regular "make clean" cleanups, but Cargo has enough smarts to really know when it can avoid recompiling.
It had also been solved since the introduction of assembly versioning over 20 years ago, which provided a standard way to have multiple versions of the same dependency installed and loaded side by side. Later Windows versions also added the ability to do the same for app-local DLLs, to the point where you can have different parts of your code get different versions of the DLL when they ask for it within the same process. So, for all practical purposes, it has been a solved problem for a very long time now.
But maybe I'm not understanding the trade offs correctly.
The counterpart is when a remotely exploitable security issue is found in that DLL, as has happened with zlib in the past and log4j more recently; then you have to chase down and update every single copy of that DLL.
But it's easier to just package a local copy and not worry about any of this, so that's what people mostly do now.