dtolnay / async-trait

Type erasure for async trait methods
Apache License 2.0
1.81k stars 84 forks source link

Road to 1.0 #122

Closed Veetaha closed 2 years ago

Veetaha commented 4 years ago

Hey, looks like the crate is mature enough for reaching 1.0. isn't it?

taiki-e commented 4 years ago

Before doing this, we might want to compare the current approach of expanding an async method into a normal method + async functions with the approach of expanding an async method into a normal method + an async block. As far as I know (and if I remember correctly), the latter can solve existing problems such as #8, #61, and #126, but it can cause some other problems such as drop-order.

EDIT: As dtolnay said in https://github.com/dtolnay/async-trait/pull/125#pullrequestreview-493609653, a normal method + an async block is not preferable if it changes the drop order.

jocutajar commented 3 years ago

I am a bit confused to be honest. I'd like to understand the real world implications of dropping unused variables. This is not destructor drop order which, I understand, is quite sensitive. If a mutex is unused, it should be dropped. And if it changes the behavior which should not be relied on in my opinion, then perhaps a major release will signal the change? Throwing away such a major improvement and simplification just because a minor change in behavior is in my opinion inadequate.

qm3ster commented 3 years ago

It is now CURRENT_YEAR, perhaps we can start preparing for min_type_alias_impl_trait? For example by having a nightly feature flag that generates code with associated types instead of Box<dyn? Something like this:

#![feature(min_type_alias_impl_trait)]
use core::future::Future;
trait Trait<'async_lifetime> {
    type F: Future<Output = String>;
    fn ie(&'async_lifetime mut self) -> Self::F;
}
struct B(String);
struct C();
impl<'async_lifetime> Trait<'async_lifetime> for B {
    type F = impl Future<Output = String> + 'async_lifetime;
    fn ie(&'async_lifetime mut self) -> Self::F {
        async move { std::mem::take(&mut self.0) }
    }
}
impl<'async_lifetime> Trait<'async_lifetime> for C {
    type F = impl Future<Output = String> + 'async_lifetime;
    fn ie(&'async_lifetime mut self) -> Self::F {
        async { "traid".into() }
    }
}
#[tokio::main]
async fn main() {
    let mut b = B("asyng".into());
    println!("{}", Bepis::ie(&mut b).await);
    println!("{}", b.0);
    println!("{}", Bepis::ie(&mut C()).await);
}

(sorry if this makes no sense, I am not a scientist)


Edit: An authoritative source has informed me that this is garbage, because it makes the trait not dynable and pollutes every constraint site with the lifetime. Those are fine tradeoffs for personally me, but definitely makes this not a 1-to-1 impl swap.

dtolnay commented 2 years ago

I think 0.1 accurately indicates the level of maturity of this crate for the foreseeable future, so I am closing this issue.