Дженерики плюс динамическая отправка - PullRequest
0 голосов
/ 28 октября 2018

Рассмотрим случай, когда у меня есть функция make_numbers, которая должна создавать строку случайных чисел, но где я хочу решить во время выполнения (пользовательский ввод), какой тип генератора случайных чисел следует использовать.Чтобы сделать это еще сложнее, давайте предположим, что функция make_numbers является общей для типа генерируемых чисел.

Я написал то, что хочу достичь с помощью псевдокода, и я понимаю, почему это не такт работа.Тем не менее, я не знаю, как может выглядеть идиоматический способ в Rust для достижения этой цели?

Мои наивные идеи были бы:

  1. Использование Box<Rng>, но это не такне работает, так как Rng имеет общие функции.
  2. Используйте перечисление для StdRng и XorShiftRng, но я не могу придумать хороший способ написать это.

Можете ли вы дать мне несколько советов о том, как будет выглядеть хорошее решение этой конкретной проблемы?

Примечание. Этот вопрос не столько о разных спичечных плечах, имеющих разные типы (решения)может быть Box или enum, как указано выше), но как применять эти решения в этом случае.

extern crate rand;

use rand::{Rng, SeedableRng, StdRng};
use rand::prng::XorShiftRng;
use std::string::String;
use rand::distributions::{Distribution, Standard};
use std::fmt::Display;

// Generic function that should work with any type of random number generator
fn make_numbers<T, R: Rng>(rng: &mut R) -> String 
    where T: Display, Standard: Distribution<T> 
{
    let mut s = String::new();
    for _i in 0..10 {
        s.push_str(format!("_{}", rng.gen::<T>()).as_str());
    }
    s
}

fn main() {
    let use_std = true; // -> assume that this will be determined at runtime (e.g. user input)

    // Pseudo code, will not work.
    let mut rng = match use_std {
        true => StdRng::from_seed(b"thisisadummyseedthisisadummyseed".to_owned()),
        false => XorShiftRng::from_seed(b"thisisadummyseed".to_owned())
    };

    let s = make_numbers::<u8>(&mut rng);

    // ... do some complex stuff with s ...

    print!("{}", s)
}
error[E0308]: match arms have incompatible types
  --> src/main.rs:24:19
   |
24 |       let mut rng = match use_std {
   |  ___________________^
25 | |         true => StdRng::from_seed(b"thisisadummyseedthisisadummyseed".to_owned()),
26 | |         false => XorShiftRng::from_seed(b"thisisadummyseed".to_owned())
   | |                  ------------------------------------------------------ match arm with an incompatible type
27 | |     };
   | |_____^ expected struct `rand::StdRng`, found struct `rand::XorShiftRng`
   |
   = note: expected type `rand::StdRng`
              found type `rand::XorShiftRng`

Ответы [ 2 ]

0 голосов
/ 28 октября 2018

Вы заметили, что не можете использовать Box<dyn Rng>, поскольку черта Rng не является объектно-безопасной.Ящик rand предлагает решение для этого, однако: Основа каждого ГСЧ обеспечивается чертой RngCore, которая является объектно-безопасной, а Box<dyn RngCore> также реализует Rng посредством этих двух реализаций черты:

Первая реализация гарантирует, что Box<dyn RngCore> сам по себе RngCore, тогда как второй реализует Rng для всех RngCore объектов.По сути, вы сможете вызывать все Rng методы для RngCore объектов признаков, и реализация динамически отправляет необходимые RngCore методы под капот.

Используя это, вы можете использоватьследующий код:

let mut rng: Box<dyn RngCore> = if use_std {
    Box::new(
        StdRng::from_seed(b"thisisadummyseedthisisadummyseed".to_owned())
    )
} else {
    Box::new(
        XorShiftRng::from_seed(b"thisisadummyseed".to_owned())
    )
};
let s = make_numbers::<u8, _>(&mut rng);
0 голосов
/ 28 октября 2018

Я думаю, вы понимаете, что типы ваших match рук должны быть одинаковыми.(В противном случае, пожалуйста, обратитесь к предложенному дубликату .)

Еще один вариант, который я вижу в вашем конкретном случае, это просто вызвать make_numbers для каждой руки:

fn main() {
    let use_std = true;     
    let s = match use_std {
        true => make_numbers::<u8, _>(&mut StdRng::from_seed(b"thisisadummyseedthisisadummyseed".to_owned())),
        false => make_numbers::<u8, _>(&mut XorShiftRng::from_seed(b"thisisadummyseed".to_owned()))
    };
    print!("{}", s)
}

Я вижу, что это может не иметь смысла, если у вас есть много дополнительных параметров в make_numbers.

В таких случаях я прибегал к макросам:

fn main() {
    let use_std = true;  
    macro_rules! call_make_numbers(($t:ty, $rng:ident, $str:expr) => {
        make_numbers::<$t, _>(&mut $rng::from_seed($str.to_owned()))
    });
    let s = match use_std {
        true => call_make_numbers!(u8, StdRng, b"thisisadummyseedthisisadummyseed"),
        false => call_make_numbers!(u8, XorShiftRng, b"thisisadummyseed"),
    };
    print!("{}", s)
}
...