I have some basic questions about how crates are supposed to work together in Rust, specifically if their public interfaces contain common, but not trivial data types. This question requires a bit of context, so I will start with an example to explain the scenario. After that I will get into the more abstract questions.
Example
An example for such a common type would be an RGB color value:
Lets assume that there is a crate that I want to use that has public functions that take an RGB value, lets call this hypothetical crate draw
(no connection to the actual draw
crate). Because the authors of the draw
did not want to re-invent the wheel, they decided to use the type rgb::RGBA
from the rgb
crate in their interface for functions such as set_color(...)
and they publicly re-export this type in their crate as well.
Let's now suppose that I want to author a second crate that can be used together with draw
but also in other scenarios. My crate (in this example called skycolor
) has a public function called get_sky_color()
that returns the current color of the sky where the program is run. Because I do not want to reinvent the wheel, either, and because I want interoperability with draw
, I also use rgb::RGBA
in my public interface (as the return type of get_sky_color()
).
Now (as far as I understand), there is a big problem: Since I do not own the draw
crate and other possible "consumers" of color values produced by skycolor
, I cannot control that skycolor
's version of the rgb
crate is the same as the one used in draw
. If someone wants to use skycolor
together with a newer version of draw
that references a newer version of rgb
, the code will not compile when you pass the return value of get_skype_color()
to set_color(...)
. Even worse: when someone wants to use skycolor
together with two crates (e.g. draw
and print
) that each reference a different version of the rgb
-crate, there is no way to have compatible interfaces!
Questions
- How should crates handle complex/composite data types in their interfaces? Should one use specialized crates such as the
rgb
crate for types or is it better when each crate defines their own types and the crate user is supposed to do conversion each time a value is passed from one crate to another? - Should a crate, if it uses such a data-type-crate, publicly re-export the type? If yes, does this mean the actual source of the type is an implementation detail of the crate and therefore there cannot be interoperability between this crate and another one that uses the same type?
- Is there a clever way to make it possible that all crates in a project actually use the same version of a data-type-crate such as
rgb
? If this is possible, then there would not be any compile time incompatibility (as long as the signature of the types themselves did not change).