Skip to content

Instantly share code, notes, and snippets.

@rust-play
Created July 7, 2025 18:56
Show Gist options
  • Select an option

  • Save rust-play/a601efc7482b38d1196eb4a3eddbf3d7 to your computer and use it in GitHub Desktop.

Select an option

Save rust-play/a601efc7482b38d1196eb4a3eddbf3d7 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
use std::sync::Arc;
struct T {}
impl T {
async fn foo(&self) {
println!("foo")
}
#[tokio::main]
async fn outer(self) {
// The requirement:
// library_fn(
// /* how to call self.foo() here in closure? */
// );
// --------------------------------------------------------
let _self = Arc::new(self);
// Variant 1.
// Err: "`_self` does not live long enough"
//
// IIUC, the async closure might (and probably will) be called AFTER
// the [library_fn] call is over, thus `_self` is dropped to that moment.
// library_fn(async || {
// _self.foo().await
// });
// --------------------------------------------------------
// Variant 2.
// Err: "async closure does not implement FnMut because it captures state from its environment (rustc)"
//
// I am not sure I get it.
// FnMut is supposed to represent the closure that might be called many times.
// And if [Arc] is "ref counter that owns the data" why it just cannot
// be used/copied while holding the ref to the data that is moved to heap (I hope?).
// What's the matter?
// library_fn(async move || {
// // _self.foo().await; // Neither this nor next works
// _self.clone().foo().await
// });
// --------------------------------------------------------
// Variant 3.
// Err (w/o move): "closure may outlive the current function,
// but it borrows `_self`, which is owned by the current function"
// With move: compilable.
// library_fn(move || {
// let _self = _self.clone();
// async {
// }
// });
// --------------------------------------------------------
// Variant 3.1.
// The closures are lazy. So, before it's called inside [library_fn],
// where is that [_self]? It's associated with the closure context
// and will not be GCed earlier?
//
// And it doesn't even have the hint like
// "function requires argument type to outlive `'static`"
// I assume because [_self] now belongs to the closure.
// So, since the closure owns it there's no borrowing, thus no 'static requirement.
//
// Although IDK then what 'static for FnMut and for returned Future mean.
// Does it prevent to pass closure by reference? Capturing the non-'static references?
//
// But I do need to call [foo], that is async, so I need async context,
// so async block (???) it's just a future constructor with the block/scope
// becoming the body of it, right? Not the inner closure?
//
// And this thing also required to be 'static.
// library_fn(move || {
// let _self = _self.clone();
// async {
// _self.foo().await
// }
// // Err:
// // async block may outlive the current function,
// // but it borrows `_self`, which is owned by the current function
// //
// // Hint:
// // to force the async block to take ownership of `_self`
// // (and any other referenced variables), use the `move` keyword
// });
// --------------------------------------------------------
// Variant 4
library_fn(move || {
let _self = _self.clone();
async move {
_self.foo().await
}
});
// This finally works.
// Why? How?
//
// What's happened with 'static requirement?
// Is it gone because it covers only references and here we are owning
// the values of smart pointers?
//
// Am I correct, that here:
// _self: Transfer ownership to closure
// _self: Transfer ownership to Future's body?
//
// Why then it wasn't working with var2?
// If the ownership could be transferred in 2 steps to closure
// and then to the Future's body, why it cannot be transferred in 1 step?
// Or what?
}
}
fn library_fn<F, T>(_: F) -> ()
where
F: 'static + FnMut() -> T + Send,
T: 'static + Future<Output = ()> + Send,
{
/* Some library code */
}
fn main() {
T{}.outer();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment