Dynamic dispatch and Sized trait


#1

I’m trying to use dynamic dispatch with the rand::Rng trait from the rand crate, and I’m running into an issue because one of the methods involves the Sized trait.

Here’s some example code:

extern crate rand;

use rand::Rng;

fn do_something(rng : &mut Rng) {
    println!("{:?}", rng.next_f64());
}

fn do_something_else(rng : &mut Rng) {
    let choices = vec![1,2,3];
    println!("{:?}", rng.choose(&choices));
}

fn main() {
    let mut rng  = rand::thread_rng();
    do_something(&mut rng);
    do_something_else(&mut rng);
}

The compiler complains about the do_something_else method with the following error message:

src/main.rs:11:26: 11:42 error: the trait `core::marker::Sized` is not implemented for the type `rand::Rng` [E0277]
src/main.rs:11     println!("{:?}", rng.choose(&choices));
                                        ^~~~~~~~~~~~~~~~

And, indeed, the choose method does have where Self: Sized.

I’d like to be able to pass a rand::ThreadRng struct as an argument to do_something_else in the production code, and a different (mock) implementation of the rand::Rng trait to do_something_else in my test code, so I can have a deterministic test case.

Is there any way I can get this type of dynamic dispatch behavior given the signature of the choose method?


#2

The choose method has Self: Sized constraint because it is generic over [T]. Without this constraint the Rng wouldn’t be object-safe, so you wouldn’t be able to pass &mut Rng around using dynamic dispatch.

The solution to your problem may be switch to static dispatch, making your functions generic over T: Rng and passing &mut T around, e.g.:

fn do_something_else<T: Rng>(rng: &mut T) {
  let choices = vec![1, 2, 3];
  println!("{:?}", rng.choose(&choices));
}

fn main() {
  let mut rng = rand::thread_rng();
  do_something_else(&mut rng);
}