我如何通过特征及其相关生命周期加入嵌套的 BoxFutures?

How can I join nested BoxFutures via traits and their associated lifetimes?

我有一个 AdoptablePet 特性,可以让你通过 fn do_adoption(id: &Self::Id) -> BoxFuture<'static, Result<Self, Self::Error>>; 异步领养宠物。

我有一个可以领养的 Dog 特性 (pub trait Dog: AdoptablePet) 并且在允许你真正领养宠物之前需要一个关联的 AdoptingPerson 和一个 adoption_policyadoption_policy 只是一个函数,它 returns 一个盒装期货数组 returning Results.

当我去创建一个 Pitbull 时,它实现了 DogAdoptablePet,一切正常,但是当我尝试默认实现 adoption_policy(因为所有 Pitbulls 都是一样的)我无法在盒装期货的所有连接之间获得正确的引用。

当我尝试 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`

我有点迷路了。如果我在 adopt 中不 join_all (并且只是 return Self::do_adoption(id) 一切正常(请参阅代码中的 [EXAMPLE A])。这是怎么回事?

代码(也available in a git repo):

#![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();
        });
    }
}

我正在使用期货预览版 0.3.0-alpha.16。

这是工作版本:

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. 必须导入
  3. futures::future::FutureExt 才能访问 FutureExt::then
  4. anyIterator 上的方法,而不是 Vec 上的方法:policy_results.iter().any(/* ... */)
  5. id 由于您的边界需要 'staticid: &'static Self::Id
  6. id 需要移动到闭包中以防止借用:move |policy_results| { /* ... */ }.

另请参阅:

  • Why do I need to import a trait to use the methods it defines for a type?