Почему свойство impl не может быть использовано для возврата нескольких / условных типов? - PullRequest
0 голосов
/ 24 августа 2018

Я пытаюсь получить генератор случайных чисел.Так как OsRng::new() может дать сбой, я бы хотел вернуться к thread_rng(), если мне нужно:

extern crate rand; // 0.6.5

use rand::{rngs::OsRng, thread_rng, RngCore};

fn rng() -> impl RngCore {
    match OsRng::new() {
        Ok(rng) => rng,
        Err(e) => thread_rng(),
    }
}

Однако я получаю это сообщение об ошибке, которое не могу понять:

error[E0308]: match arms have incompatible types
 --> src/lib.rs:6:5
  |
6 | /     match OsRng::new() {
7 | |         Ok(rng) => rng,
8 | |         Err(e) => thread_rng(),
  | |                   ------------ match arm with an incompatible type
9 | |     }
  | |_____^ expected struct `rand::rngs::OsRng`, found struct `rand::prelude::ThreadRng`
  |
  = note: expected type `rand::rngs::OsRng`
             found type `rand::prelude::ThreadRng`

Почему компилятор ожидает rand::OsRng здесь вместо реализации RngCore?Если я удаляю match и напрямую возвращаю thread_rng(), я не получаю сообщение об ошибке выше.

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

Ответы [ 2 ]

0 голосов
/ 28 марта 2019

DK. уже объяснил почему , но я бы хотел предложить альтернативное решение.

Как уже упоминалось в Условно итерируя по одному из нескольких возможных итераторов , вы можете создать перечисление, которое реализует признак, если оба его типа компонентов это делают. Например:

extern crate rand; // 0.6.5

use rand::{rngs::OsRng, thread_rng, RngCore};

fn rng() -> impl RngCore {
    match OsRng::new() {
        Ok(rng) => EitherRng::Left(rng),
        Err(_) => EitherRng::Right(thread_rng()),
    }
}

enum EitherRng<L, R> {
    Left(L),
    Right(R),
}

impl<L, R> RngCore for EitherRng<L, R>
where
    L: RngCore,
    R: RngCore,
{
    fn next_u32(&mut self) -> u32 {
        match self {
            EitherRng::Left(l) => l.next_u32(),
            EitherRng::Right(r) => r.next_u32(),
        }
    }

    fn next_u64(&mut self) -> u64 {
        match self {
            EitherRng::Left(l) => l.next_u64(),
            EitherRng::Right(r) => r.next_u64(),
        }
    }

    fn fill_bytes(&mut self, b: &mut [u8]) {
        match self {
            EitherRng::Left(l) => l.fill_bytes(b),
            EitherRng::Right(r) => r.fill_bytes(b),
        }
    }

    fn try_fill_bytes(&mut self, b: &mut [u8]) -> Result<(), rand::Error> {
        match self {
            EitherRng::Left(l) => l.try_fill_bytes(b),
            EitherRng::Right(r) => r.try_fill_bytes(b),
        }
    }
}

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

Смотри также:

0 голосов
/ 25 августа 2018

impl Trait не эквивалентно возвращению интерфейса или объекта базового класса. Это способ сказать: «Я не хочу писать имя определенного типа, который я возвращаю». Вы все еще возвращаете значение одного, определенного типа; Вы просто не говорите , который печатает.

Каждая из этих ветвей возвращает разные типы, отсюда и проблема. Реализации той же черты недостаточно.

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

extern crate rand; // 0.6.5

use rand::{rngs::OsRng, thread_rng, RngCore};

fn rng() -> Box<dyn RngCore> {
    match OsRng::new() {
        Ok(rng) => Box::new(rng),
        Err(_) => Box::new(thread_rng()),
    }
}

Примечание : если вы используете чуть более старую версию Rust, вам может потребоваться удалить ключевое слово dyn. Это необязательно в текущем (2015) выпуске Rust.

...