0

I have the following code, where I try to implement Rng and RngCore for a custom type.

#[cfg(test)]
use rand::{Rng, RngCore};

pub const FP751_NUM_WORDS: usize = 12;

#[repr(C)]
#[derive(Copy, Clone)]
pub struct Fp751Element(pub (crate) [u64; FP751_NUM_WORDS]);

#[cfg(test)]
impl RngCore for Fp751Element {
    fn next_u64(&mut self) -> u64 {
        self.next_u64()
    }
}

#[cfg(test)]
impl Rng for Fp751Element {
    fn gen<Fp751Element>(&mut self) -> Fp751Element {
        let high_limb = self.next_u64() % 246065832128056;

        Fp751Element([
            self.next_u64(),
            self.next_u64(),
            self.next_u64(),
            self.next_u64(),
            self.next_u64(),
            self.next_u64(),
            self.next_u64(),
            self.next_u64(),
            self.next_u64(),
            self.next_u64(),
            self.next_u64(),
            high_limb
        ])
    }
}

I want to implement the traits because I later want to use the quickcheck::Arbitrary with my type. However, the above type code throws the following error at impl Rng for Fp751Element line:

conflicting implementation in crate rand: - impl rand::Rng for R where R: rand::RngCore, R: ?Sized;

Any ideas?

terett
  • 175
  • 4

1 Answers1

8

tl;dr That is the wrong trait to implement if you want to generate random values of your own type.

The trait Rng is an extension trait for RngCore, with a blanket implementation included. This means that all types which implement RngCore already implement Rng, and you should not try to provide it yourself. Once Rng is put into scope, all implementations of RngCore will have the extra methods provided there.

However:

I want to implement the traits because I later want to use the quickcheck::Arbitrary with my type.

So this is an XY problem! RngCore is designed to create random number generation algorithms, not to define how data types are sampled from a random number source.

fn gen<Fp751Element>(&mut self) -> Fp751Element {

In fact, the method signature given is very misleading. In this context, Fp751Element is not your struct type Fp751Element, but a new generic parameter type with the same name!

In order to implement the generation of your own type with your own distributon, provide a custom Distribution implementation.

struct Fp751UniformDist;

impl Distribution<Fp751Element> for Fp751UniformDist {
    fn sample<R: Rng + ?Sized>((&self, rng: &mut R) -> Fp751Element {
        let high_limb = rng.next_u64() % 246065832128056;

        Fp751Element([
            rng.next_u64(),
            rng.next_u64(),
            rng.next_u64(),
            rng.next_u64(),
            rng.next_u64(),
            rng.next_u64(),
            rng.next_u64(),
            rng.next_u64(),
            rng.next_u64(),
            rng.next_u64(),
            rng.next_u64(),
            high_limb
        ])
    }
}

With this done, one would produce new values of your type using sample:

let mut rng = thread_rng();
let elem: Fp751Element = rng.sample(Fp751UniformDist);

See also:

E_net4 - Krabbe mit Hüten
  • 24,143
  • 12
  • 85
  • 121