Search code examples
rustdependenciestraitssemantic-versioningtransitive-dependency

How can I use a trait from a transitive dependency in Rust?


How can I use a trait from a transitive dependency crate in my application?

Here's a minimal illustrative example of the problem I've run into:

In Cargo.toml, I have:

[dependencies]
mersenne_twister = "1.1.1"
rand = "0.8.5"

My crate depends on rand ^0.8.5 and mersenne_twister ^1.1.1, which itself depends on rand >=0.3, <0.5:

my-crate ---> rand 0.8.5
         |
         |
         ---> mersenne_twister 1.1.1  ----> rand >= 0.3, < 0.5

In my app, I'd like to use the implementation of trait rand::Rng for mersenne_twister::MT19937. But when I try bringing this trait into scope, it's apparently not recognized:

use mersenne_twister::MT19937;
use rand::Rng;

fn main() {
    let mut rng = MT19937::new_unseeded();
    let mut buffer = vec![0; 0xFFFF];

    // error[E0599]: no method named `fill_bytes` found for struct `MT19937` in the current scope
    rng.fill_bytes(&mut buffer);
}

My guess is that the Rng trait imported by use rand::Rng; is the one from rand 0.8.5, not the one from rand 0.4.6 that is actually implemented for MT19937, and that even though they are spelled the same way, they are distinct and unrelated traits and therefore cannot be referenced interchangeably.


So I have some questions:

  1. How can I use the Rng trait that works for MT19937 in my app? I don't think I can downgrade my dependency on rand to 0.4.6 in Cargo.toml because I need to use rand 0.8.5 elsewere in my app.
  2. In general how should traits defined in transitive dependencies be used?
  3. Is mersenne_twister's API design bad practice for not re-exporting Rng?

Solution

  • You can add multiple incompatible versions of a dependency to Cargo.toml by renaming one of them.

    [dependencies]
    mersenne_twister = "1.1.1"
    rand = "0.8.5"
    old_rand = { package = "rand", version = "0.4.6" }
    

    But for this situation, there's already a better solution: use the MT19937 crate, which depends on the latest version of rand_core. RNG implementations should depend on rand_core instead of rand since all they need is the Rng trait and not any of the extras in rand, like distributions.