Calling async callback with reference (lifetime trouble)

Hey guys, I'm going further with my studies,

I could implement:

  • An async function that receives an async callback which consumes it arguments :white_check_mark:
  • An async function that receives an synchronous callback which receives a reference (borrowing) :white_check_mark:

I couldn't implement:

  • Async function that receives an async callback which borrow it's arguments :x:

I'm using a recursive walkdir as case of study, it receives a path to start, and a callback, it calls the callback for every file it founds, and recurse it in folders that it find .. is a good exercise and became complex fast :slight_smile:

Here is the working code (synchronous callback)

And here is the not working code (async callback)

I tried that 'b lifetime parameter to say "Hey, &DirEntry will live as long as my returned Future, but it seems that this is not the case since, it won't compile, I now I'm stuck, again

Is what I'm trying to do, possible?
How can I express that "I have a callback that receives a reference and return a future and that the reference lives lesser than my outer function, but enough for the callback"?

In other words, how can I express lifetimes for Fn async callbacks parameters?

Sorry, when working with closures, it's not possible for the output to borrow from the input. The easiest option is to just not create a future that needs to borrow from the input, by first creating a value that has ownership and then moving it into the future instead:

walkdir(Path::new(".").as_ref(), |d: &DirEntry| {
    let s = d.file_name().into_string().unwrap();
    async move {
        println!("{}", s);
    }
}).await;

playground

4 Likes

Hi Alice thank you so much

I didn't get the output to borrow, what output means here, the Future? The future is borrowing from the closure parameters, do I get this right?

With "output", I am referring to the value of type Fut returned by your closure. I am saying that this returned value of type Fut is not able to borrow from the reference passed to the closure, because if it could, the closure would return a different type depending on which lifetime the reference you gave the closure has.

I got it, so Fn(&DirEntry) -> Fut

If Fut depends on &DirEntry, each possible reference lifetime would lead to a distinct Fut concrete type

Now lifetimes being generic types start to make some sense to me. When I say foo<'a>(bar: &'a) each application of 'a type leads is like a kind of type application (I'm ignoring elision here), so

let a = &1; { let b = &2; foo(&a); foo(&b); }

Is really like applying concrete types to a generic function, except that the compiler will do borrow checking instead of type checking, did I get it right? :smiley:

It's seems so obvious now

2 Likes

This is exactly right!

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.