Skip to content

Instantly share code, notes, and snippets.

@rust-play
Created May 26, 2020 18:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rust-play/b89d17f00f58f071555339093d76a0a3 to your computer and use it in GitHub Desktop.
Save rust-play/b89d17f00f58f071555339093d76a0a3 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
// This is related to https://www.reddit.com/r/rust/comments/goh2be/how_to_store_an_async_future_to_a_function_with_a/
#[derive(Debug)]
struct MyStruct {
a: usize,
b: usize,
c: String,
// ...
}
struct StructWithCallback<T> {
callback: T,
}
impl<T> StructWithCallback<T> {
fn new(cb: T) -> Self {
Self {
callback: cb
}
}
}
async fn my_function_0<'r>(my_struct: &'r mut MyStruct) {
println!("in my_function_0: {:?}", my_struct);
// Change stuff.
my_struct.a += 1;
my_struct.b += 2;
my_struct.c = "a different string".to_string();
}
async fn my_function_1<'r>(my_struct: &'r mut MyStruct) {
println!("in my_function_1: {:?}", my_struct);
// Change stuff.
my_struct.a += 1;
my_struct.b += 2;
my_struct.c = "a completely different string".to_string();
}
async fn example() {
let mut callbacks = vec![
StructWithCallback::new(my_function_0),
StructWithCallback::new(my_function_1)
];
let mut my_struct = MyStruct {
a: 1,
b: 2,
c: "a string".to_string(),
};
// Invoke the stored callbacks.
let callback_0 = callbacks[0].callback;
callback_0(&mut my_struct).await;
let callback_1 = callbacks[0].callback;
callback_1(&mut my_struct).await;
// Confirm stuff changed.
println!("after my_function: {:?}", my_struct);
}
fn main() {
let mut rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(example());
}
@alsuren
Copy link

alsuren commented May 26, 2020

@LionsAd Unfortunately, making StructWithCallback means that you can't store it in a vec or hashmap. This causes:

error[E0308]: mismatched types
  --> src/main.rs:42:33
   |
42 |         StructWithCallback::new(my_function_1)
   |                                 ^^^^^^^^^^^^^ expected fn item, found a different fn item
   |
   = note: expected fn item `for<'r> fn(&'r mut MyStruct) -> impl std::future::Future {my_function_0}`
              found fn item `for<'r> fn(&'r mut MyStruct) -> impl std::future::Future {my_function_1}`

Playground is here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b89d17f00f58f071555339093d76a0a3

Let's keep discussion on here, to avoid spamming the issue too much.

@LionsAd
Copy link

LionsAd commented May 27, 2020

Yeah - I see. Due to the type interference it assigns it the concrete type.

@LionsAd
Copy link

LionsAd commented May 27, 2020

How does Box::pin avoid that problem - can't a struct do the same thing?

@alsuren
Copy link

alsuren commented May 27, 2020

The trick we are using in goose returns a Pin<Box<dyn Future<Output=()>>>, which is a concrete type (Box<dyn ...>) with a concrete in-memory layout (fat pointer to (Future object, vtable for all the methods in the Future trait)) so it can safely be stored in a vec.

The dyn keyword is the crucial bit here: it implies dynamic dispatch, using a vtable.

Boxing also gets around the fact that the different Future implementations may be differently sized. I generally expect to see Box and dyn together. Box coerces the differently-sized objects into a single size (a pointer to the object on the heap) and the other coerces the different implementations of the Trait into a single thing (pointer to a vtable).

@alsuren
Copy link

alsuren commented May 27, 2020

I answered the wrong question there...

The reason that Box<dyn Trait> works for goose is that it has space in the syntax for the for<'r> lifetime to be applied to the Future (using +). When I tried to change it into a type parameter, I had nowhere to put the lifetime, and so the borrow checker got very sad.

@alsuren
Copy link

alsuren commented Jul 1, 2020

@LionsAd: https://gist.github.com/87ab93979d770314e6698a9867d1e7e5 does what we want. I will have a go at patching Goose to use this pattern.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment