#future #scope #hrtb #lifetime #scoped-futures #liftime

no-std scoped-futures

A utility crate for imposing upper bounds on Future lifetimes

5 releases

0.1.4 Oct 23, 2024
0.1.3 Jan 2, 2023
0.1.2 Nov 8, 2022
0.1.1 Nov 8, 2022
0.1.0 Nov 8, 2022

#104 in Asynchronous

Download history 11796/week @ 2024-12-25 22309/week @ 2025-01-01 36831/week @ 2025-01-08 38495/week @ 2025-01-15 40103/week @ 2025-01-22 40518/week @ 2025-01-29 50371/week @ 2025-02-05 46992/week @ 2025-02-12 49806/week @ 2025-02-19 51975/week @ 2025-02-26 47579/week @ 2025-03-05 45756/week @ 2025-03-12 43211/week @ 2025-03-19 43326/week @ 2025-03-26 46061/week @ 2025-04-02 38382/week @ 2025-04-09

179,619 downloads per month
Used in 55 crates (7 directly)

MIT/Apache

13KB
118 lines

scoped-futures

A utility crate for imposing upper bounds on Future lifetimes. This is especially useful for callbacks that use higher-ranked lifetimes in their return type, where it can prevent 'static bounds from being placed on a returned Future.

This crate is effectively a port of Sabrina Jewson's better alternative to lifetime GATs for Futures.

Example

use core::pin::Pin;
use scoped_futures::{ScopedBoxFuture, ScopedFutureExt};

pub struct Db {
    count: u8,
}

impl Db {
    async fn transaction<'a, F, T, E>(&mut self, callback: F) -> Result<T, E>
    where
        // ScopedBoxFuture imposes a lifetime bound on 'b which prevents the hrtb below needing
        // to be satisfied for all lifetimes (including 'static) and instead only lifetimes
        // which live at most as long as 'a
        F: for<'b /* where 'a: 'b */> FnOnce(&'b mut Self) -> ScopedBoxFuture<'a, 'b, Result<T, E>> + Send + 'a,
        T: 'a,
        E: 'a,
    {
        callback(self).await
    }
}`

pub async fn test_transaction<'a, 'b>(
    db: &mut Db,
    ok: &'a str,
    err: &'b str,
    is_ok: bool,
) -> Result<&'a str, &'b str> {
    // note the lack of `move` or any cloning in front of the closure
    db.transaction(|db| async move {
        db.count += 1;
        if is_ok {
            Ok(ok)
        } else {
            Err(err)
        }
    }.scope_boxed()).await?;

    // note that `async` can be used instead of `async move` since the callback param is unused
    db.transaction(|_| async {
        if is_ok {
            Ok(ok)
        } else {
            Err(err)
        }
    }.scope_boxed()).await
}

#[test]
fn test_transaction_works() {
    futures::executor::block_on(async {
        let mut db = Db { count: 0 };
        let ok = String::from("ok");
        let err = String::from("err");
        let result = test_transaction(&mut db, &ok, &err, true).await;
        assert_eq!(ok, result.unwrap());
        assert_eq!(1, db.count);
    })
}

Dependencies

~46KB