Как я могу присоединиться к вложенным BoxFutures через черты и связанные с ними времена жизни? - PullRequest
0 голосов
/ 18 июня 2019

У меня есть черта AdoptablePet, которая позволяет вам асинхронно перенимать питомца с помощью fn do_adoption(id: &Self::Id) -> BoxFuture<'static, Result<Self, Self::Error>>;.

У меня есть черта Dog, которая является приемлемой (pub trait Dog: AdoptablePet) и принимает связанную AdoptingPerson и adoption_policy, прежде чем позволить вам фактически усыновить питомца.adoption_policy - это просто функция, которая возвращает массив упакованных фьючерсов, возвращающих Result s.

Когда я собираюсь создать Pitbull, который реализует как Dog, так и AdoptablePet, всеработает, но как только я пытаюсь сделать реализацию по умолчанию adoption_policy (так как она будет одинаковой для всех Pitbull s), я не могу получить правильные ссылки между всеми объединениями в штучной упаковке.

Когда я пытаюсь join_all adoption_policy Vec, он содержит ссылки на коробочные фьючерсы, а не на сами коробочные фьючерсы.Когда я пытаюсь отобразить и разыменовать их, я получаю ошибку проверки заимствования (см. [EXAMPLE B] в коде):

error[E0277]: the trait bound `&std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = std::result::Result<(), AdoptionError>> + std::marker::Send>>: core::future::future::Future` is not satisfied
  --> src/lib.rs:70:13
   |
70 |             join_all(Self::adoption_policy(adopter, id).iter()).then(|policy_results| {
   |             ^^^^^^^^ the trait `core::future::future::Future` is not implemented for `&std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = std::result::Result<(), AdoptionError>> + std::marker::Send>>`
   |
   = help: the following implementations were found:
             <std::pin::Pin<P> as core::future::future::Future>
   = note: required by `futures_util::future::join_all::join_all`

error[E0599]: no method named `then` found for type `futures_util::future::join_all::JoinAll<&std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = std::result::Result<(), AdoptionError>> + std::marker::Send>>>` in the current scope
  --> src/lib.rs:70:65
   |
70 |             join_all(Self::adoption_policy(adopter, id).iter()).then(|policy_results| {
   |                                                                 ^^^^

error[E0277]: the trait bound `&std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = std::result::Result<(), AdoptionError>> + std::marker::Send>>: core::future::future::Future` is not satisfied
  --> src/lib.rs:70:13
   |
70 |             join_all(Self::adoption_policy(adopter, id).iter()).then(|policy_results| {
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `core::future::future::Future` is not implemented for `&std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = std::result::Result<(), AdoptionError>> + std::marker::Send>>`
   |
   = help: the following implementations were found:
             <std::pin::Pin<P> as core::future::future::Future>
   = note: required by `futures_util::future::join_all::JoinAll`

Я немного растерялся.Если я не join_all в adopt (и просто возвращаю Self::do_adoption(id), все работает (см. [EXAMPLE A] в коде). Что происходит?

Код (также доступен врепозиторий git ):

#![feature(async_await)]
use futures::future::{self, join_all, BoxFuture};

#[derive(Debug)]
pub struct AdoptionError;

pub trait AdoptablePet
where
    Self: Sized,
{
    /// The id of the pet to adopt.
    type Id;

    /// Adopt the pet.
    fn do_adoption(id: &Self::Id) -> BoxFuture<'static, Result<Self, AdoptionError>>;
}

pub trait Dog: AdoptablePet
where
    // XXX: Are these all needed?
    Self: Sized + Send,
    <Self as AdoptablePet>::Id: Sync,
    Self: 'static,
    Self::AdoptingPerson: Sync,
{
    /// The Person adopting a dog.
    type AdoptingPerson;

    /// The policy to check when a person is adopting a particular dog.
    fn adoption_policy(
        adopter: &Self::AdoptingPerson,
        id: &Self::Id,
    ) -> Vec<BoxFuture<'static, Result<(), AdoptionError>>>;

    /// Policy-aware adoption.
    fn adopt(
        adopter: &Self::AdoptingPerson,
        id: &Self::Id,
    ) -> BoxFuture<'static, Result<Self, AdoptionError>> {
        // [EXAMPLE A]
        // Doing the following works...
        /*
        if true {
            Self::do_adoption(id)
        } else {
            Box::pin(future::ready(Err(AdoptionError{})))
        }
        */

        /* [EXAMPLE B]
           But this is what I want to do. This is the error:

            --> src/lib.rs:71:13
             |
          71 | /             join_all(
          72 | |
          73 | |
          74 | |                 --> src/lib.rs:65:13
          ...  |
          86 | |                 Self::adoption_policy(adopter, id).iter(),
          87 | |             )
             | |_____________^ the trait `core::future::future::Future` is not implemented for `&std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = std::result::Result<(), AdoptionError>> + std::marker::Send>>`
             |
             = help: the following implementations were found:
                       <std::pin::Pin<P> as core::future::future::Future>
             = note: required by `futures_util::future::join_all::JoinAll`
        */
        Box::pin(
            // Check all the adoption rules in the policy.
            join_all(Self::adoption_policy(adopter, id).iter()).then(|policy_results| {
                // Depending on the result, do the (async/long-running)
                // adoption or return an error.
                let has_policy_failure = policy_results.any(|x| x.is_err());
                if !has_policy_failure {
                    Self::do_adoption(id)
                } else {
                    Box::pin(future::ready(Err(AdoptionError {})))
                }
            }),
        )
    }
}

/// Implementation.

#[derive(Debug, Clone, PartialEq)]
pub struct DogId(pub String);

pub struct Pitbull {
    pub id: DogId,
}

impl AdoptablePet for Pitbull {
    type Id = DogId;

    fn do_adoption(id: &Self::Id) -> BoxFuture<'static, Result<Self, AdoptionError>> {
        Box::pin(future::ready(Ok(Pitbull { id: id.clone() })))
    }
}

impl Dog for Pitbull {
    type AdoptingPerson = Person;
    fn adoption_policy(
        _adopter: &Self::AdoptingPerson,
        _id: &Self::Id,
    ) -> Vec<BoxFuture<'static, Result<(), AdoptionError>>> {
        vec![
            // 1. Check if they have had their shots.
            // 2. Check if the adopter has children and if the breed is good with children.
            // etc.
        ]
    }
}

pub struct Person {
    name: String,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        futures::executor::block_on(async {
            let id = DogId("fluffy123".to_string());
            let adopter = Person {
                name: "Fred".to_string(),
            };
            let _ = Pitbull::adopt(&adopter, &id).await.unwrap();
        });
    }
}

Я использую futures-preview версию 0.3.0-alpha.16.

1 Ответ

1 голос
/ 19 июня 2019

Вот рабочая версия:

fn adopt(
    adopter: &Self::AdoptingPerson,
    id: &'static Self::Id,
) -> BoxFuture<'static, Result<Self, AdoptionError>> {
    Box::pin(
        join_all(Self::adoption_policy(adopter, id)).then(move |policy_results| {
            let has_policy_failure = policy_results.iter().any(|x| x.is_err());
            if !has_policy_failure {
                Self::do_adoption(id)
            } else {
                Box::pin(future::ready(Err(AdoptionError {})))
            }
        }),
    )
}

Изменения:

  1. join_all необходимо вступить во владение фьючерсами, а не ссылками на них: join_all(Self::adoption_policy(adopter, id)).
  2. futures::future::FutureExt необходимо импортировать, чтобы получить доступ к FutureExt::then.
  3. any - это метод для Iterator, а не Vec: policy_results.iter().any(/* ... */)
  4. id должно быть 'static из-за ваших границ: id: &'static Self::Id
  5. id необходимо переместить в закрытие для предотвращения заимствования: move |policy_results| { /* ... */ }.

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

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