My interpretation which could be wrong, is that the idea is help to solve issues, like, if crate A has B and C as a dep, but B also has C as a dep, C from A and C from B maybe are not compatible because of difference on their versions, so the idea is be able to have one C, which can be used in A and B.
There is a example in the RFC, where the crate onedep has the url crate marked as public (check the text after the Cargo.toml to see how to change it) and this one is used in twodep crate, but reading the code, twodep also has the url crate, so I really can't get very well how this feature is working when url also has different versions on both crates....
I'm not entirely familiar with the status of this feature, but I have used it a bit, and I believe that the only thing it does right now is produce warnings when your crate exports an item from a dependency that you did not mark as public. This is quite useful for helping prevent mistakes in your library API design, but it doesn't have any effects on version resolution or publishing. I could be wrong.
in that case I really misunderstood it, that would not solve the A/B/C case, because internally still can be uses of functions of C and mix the versions.
So this would mainly works when we export elements from a dep and make them available on our own crate? also I tried to expose a full crate from a dep, but can't be done if the dep it self is not added on A.
It only matters whether A and B use different versions of C if B's API contains a type/trait that was defined by C. With public/private dependency checking, the author of B will get a warning if they do this and don't declare their dependency on C public. The warning doesn't solve any problems itself, but it's a reminder to the author of B to think about whether they can design their API not to expose C.
B can reexport the crate — write pub use C; in B, and refer to it as B::C from within A. This can be useful in straight-line cases because it means that A doesn't have to write a dependency on C that matches B's, but strictly speaking it never does anything that couldn't be done another way.
I was thinking from the start, why this is a feature and not a automated check, is there something that can not be automatically be checked for this type of incompatibility?
If the program doesn't compile, you already get a message saying that two versions might be involved.
If the program does compile, there is currently (on stable) no automated way to distinguish between:
Two parts of the program are using two versions for different purposes that do not influence each other, and so there is no requirement for them to be the same.[1]
Future extensions to the program are going to bring those parts together, which will result in failures, and we’d like to get an error or warning sooner rather than later so we can become aware of the problem and decide what to do about it.
Two parts of the program are using two versions in a way that doesn't cause a type error but does cause a semantic error (e.g. two Tokio runtimes being the classic example).
The point of public dependency annotations is to distinguish case 1 from cases 2 and 3. Cargo needs more information about the programmers’ intent to be able to automatically check.
We don't want to make case 1 an error, because it would make upgrading commonly shared utility dependencies like itertools and syn very hard. Public dependencies, according to the RFC, will allow Cargo to warn or error on cases 2 and 3, or in some cases perform a version resolution that makes the program work. That part isn't implemented yet, I believe.
Here, using a single version might be desirable to make the binary smaller and the build faster, but it doesn’t affect correctness. ↩︎