dtolnay / async-trait

Type erasure for async trait methods
Apache License 2.0
1.84k stars 85 forks source link

Unexpected "one type is more general than the other" compiler error #144

Closed gsson closed 1 year ago

gsson commented 3 years ago

I have some async code with a collection of futures that I have called join_all on. It seems to work fine, but when I try to call that code call from an implementation from an #[async_trait], I am getting an error I don't quite understand;

error[E0308]: mismatched types
  --> src/lib.rs:50:39
   |
50 |       async fn create_trait(&self) -> V {
   |  _______________________________________^
51 | |         // This compiles
52 | |         // self.create_impl_join_all().await
53 | |         // This compiles
...  |
56 | |         self.create_impl_map_join_all().await
57 | |     }
   | |_____^ one type is more general than the other
   |
   = note: expected type `FnOnce<(V,)>`
              found type `FnOnce<(V<Box<dyn std::fmt::Display + std::marker::Send>>,)>`

The below code compiles if either of the map or join_all are called, but fails with the above error if both map and join_all are used.

In addition

use async_trait::async_trait;
use futures::future::join_all;
use futures::FutureExt;
use std::fmt::Display;

pub struct V<T = Box<dyn Display + Send>> {
    _v: T
}

fn create_v(n: usize) -> V {
    V {
        _v: Box::new(n)
    }
}

async fn create_v_async(n: usize) -> V {
    create_v(n)
}

pub struct Test {}

impl Test {
    pub async fn create_impl_join_all(&self) -> V {
        let future = create_v_async(42);
        join_all(vec![future]).await.pop().unwrap()
    }

    pub async fn create_impl_map(&self) -> V {
        let future = create_v_async(42)
            .map(|rv| rv);
        future.await
    }

    pub async fn create_impl_map_join_all(&self) -> V {
        let future = create_v_async(42)
            .map(|rv| rv);
        join_all(vec![future]).await.pop().unwrap()
    }
}

#[async_trait]
pub trait Trait<T> {
    async fn create_trait(&self) -> T;
}

#[async_trait]
impl Trait<V> for Test {
    async fn create_trait(&self) -> V {
        // This compiles
        // self.create_impl_join_all().await
        // This compiles
        // self.create_impl_map().await
        // This fails to compile with a "one type is more general than the other" error
        self.create_impl_map_join_all().await
    }
}

Potentially related to #34 ?

chpio commented 3 years ago

getting a similar error for this code

   --> crdt-enc-tokio/src/lib.rs:225:49
    |
225 |       ) -> Result<Vec<(Uuid, u64, VersionBytes)>> {
    |  _________________________________________________^
226 | |         async fn get_entry(
227 | |             path: &Path,
228 | |             actor: Uuid,
...   |
277 | |             .await
278 | |     }
    | |_____^ one type is more general than the other
    |
    = note: expected opaque type `impl futures::Future`
               found opaque type `impl futures::Future`

async-trait@0.1.42 seems to work for me, all newer versions (including 0.1.48) fail to build.

tdyas commented 3 years ago

I am seeing the same error when trying to upgrade async-trait in https://github.com/pantsbuild/pants from v0.1.42 to v0.1.43 and newer (starts to happen in v0.1.43).

   Compiling fs v0.0.1 (XXX/pants/src/rust/engine/fs)
error[E0308]: mismatched types
   --> fs/src/lib.rs:691:95
    |
691 |     async fn path_stats(&self, paths: Vec<PathBuf>) -> Result<Vec<Option<PathStat>>, io::Error> {
    |  _______________________________________________________________________________________________^
692 | |     future::try_join_all(
693 | |       paths
694 | |         .into_iter()
...   |
715 | |     .await
716 | |   }
    | |___^ one type is more general than the other
    |
   ::: /Users/XXX/.rustup/toolchains/1.54.0-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/future/mod.rs:61:43
    |
61  |   pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
    |                                             -------------------------------
    |                                             |
    |                                             the expected opaque type
    |                                             the found opaque type
    |
    = note: expected opaque type `impl futures::Future`
               found opaque type `impl futures::Future`
stuhood commented 2 years ago

I am seeing the same error when trying to upgrade async-trait in https://github.com/pantsbuild/pants from v0.1.42 to v0.1.43 and newer (starts to happen in v0.1.43).

The above issue still reproduces in 0.1.53.

dtolnay commented 1 year ago

Updated output on newer rustc:

error: higher-ranked lifetime error
  --> src/main.rs:50:39
   |
50 |       async fn create_trait(&self) -> V {
   |  _______________________________________^
51 | |         // This compiles
52 | |         // self.create_impl_join_all().await
53 | |         // This compiles
...  |
56 | |         self.create_impl_map_join_all().await
57 | |     }
   | |_____^
   |
   = note: could not prove `Pin<Box<[async block@src/main.rs:50:39: 57:6]>>: CoerceUnsized<Pin<Box<(dyn futures::Future<Output = V<Box<(dyn std::fmt::Display + std::marker::Send + 'e)>>> + std::marker::Send + 'f)>>>`

This is a rustc bug that is already reported in https://github.com/rust-lang/rust/issues/82921.