Как принять "Коробку"`в местах, которые принимают` Box`? - PullRequest
0 голосов
/ 27 мая 2019

Могу ли я принять Box<dyn Error + Send> в местах, которые принимают Box<dyn Error>? Если да, то как? Если нет, то почему, и есть ли более элегантный способ сделать это, чем в следующем примере?

#![allow(unused)]

use std::error::Error as StdError;
use std::result::Result as StdResult;

type    Result = StdResult<(), Box< dyn StdError >>;
type SndResult = StdResult<(), Box< dyn StdError + Send >>;

fn fn_returning_result()    ->    Result { Ok(()) }
fn fn_returning_sndresult() -> SndResult { Ok(()) }

/// Accept a callback that returns a non-Send `Result`.
fn register_callback<CB: FnOnce() -> Result>(cb: CB) { /* ... */ }

fn main() -> Result {
    // Is there a way to get rid of ... vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ... this part?
    let cb = || fn_returning_sndresult().map_err(|e| -> Box<dyn StdError> { e });
    register_callback(cb);

    Ok(())
}

Игровая площадка

Edit: то, что я хотел бы сделать здесь, это просто let cb = || fn_returning_sndresult();, но это не скомпилируется.

Кроме того, я хотел бы позвонить либо fn_returning_result()?, либо fn_returning_sndresult()? внутри функции обратного вызова с одним типом возврата и без выполнения map_err.

Ответы [ 2 ]

0 голосов
/ 29 мая 2019

В: Могу ли я принять Box<dyn Error + Send> в местах, которые принимают Box<dyn Error>?

A: Да, с упаковкой нового типа. Решение, которое я наконец-то нашел, заключалось в следующем: ( Детская площадка )

use std::error::Error as StdError;
use std::result::Result as StdResult;
use std::fmt;

type    Result = StdResult<(), Box< dyn StdError >>;
type SndResult = StdResult<(), Box< dyn StdError + Send >>;

fn fn_returning_result()    ->    Result { dbg!(Err("oops".into())) }
fn fn_returning_sndresult() -> SndResult { dbg!(Ok(())) }

/// Result type using `Er` defined below.
type Rt<T = ()> = StdResult<T, Er>;


// Error wrapper

#[derive(Debug)]
struct Er(Box<dyn StdError>);

impl fmt::Display for Er {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self, f)
    }
}

impl StdError for Er {}


// Allow `?` operator

impl From<Box<dyn StdError>> for Er {
    fn from(err: Box<dyn StdError>) -> Self {
        Er(err)
    }
}

impl From<Box<dyn StdError + Send>> for Er {
    fn from(err: Box<dyn StdError + Send>) -> Self {
        Er(err)
    }
}


// List of callbacks

struct Callbacks<'a>(Vec<Box<dyn FnOnce() -> Rt + 'a>>);

impl<'a> Callbacks<'a> {
    fn new() -> Self {
        Callbacks(Vec::new())
    }

    fn add(&mut self, cb: impl FnOnce() -> Rt + 'a) {
        self.0.push(Box::new(cb));
    }

    fn pop_and_call(&mut self) -> Rt {
        self.0.pop().unwrap()()
    }
}


// Example

fn main() -> Result {
    let mut callbacks = Callbacks::new();

    callbacks.add(|| { Ok(fn_returning_result()?) });
    callbacks.add(|| { Ok(fn_returning_sndresult()?) });

    callbacks.pop_and_call()?;
    callbacks.pop_and_call()?;

    Ok(())
}

Обратите внимание, что обратные вызовы могут использовать ? для значений Result или SndResult (благодаря признаку From), и что их возвращаемые значения также действительны Result s, что позволяет main также использовать ? в своих вызовах (благодаря признаку StdError impl).

0 голосов
/ 27 мая 2019

Это основано на ответе, который я получил на Reddit от Llaurence _:

Я могу изменить register_callback для принятия ошибок Box<dyn Error> и Box<dyn Error + Send>, сделав тип ошибки универсальным:

fn register_callback<CB, E>(cb: CB)
where
    CB: FnOnce() -> StdResult<(), Box<E>>,
    E: StdError + ?Sized,
{ /* ... */ }

Теперь я могу передать обратный вызов, который возвращает любой вид ошибки / результата:

register_callback(fn_returning_result);
register_callback(fn_returning_sndresult);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...