The box() method for returning futures does cause an additional allocation to the heap. Another method of returning futures relies on using a nightly version of Rust or for this issue https://github.com/rust-lang/rust/issues/34511 to be resolved. The new async_add_points() method would return an implied Future trait and would look as follows:
fn async_add_points<F>(f: F, player: &mut Player, points: u32) -> impl Future<Item = Player, Error = F::Error>
where F: Future<Item = Player>,
{
// Presuming that player.add_points() will send the points to a
// database/server over a network and returns
// an updated player score from the server/database.
let _ = player.add_points(points).flatten();
// Additionally, we may want to add logging mechanisms, friend
notifications, etc. here.
return f.map(player.clone());
}
Rust may cause undefined behavior if we were to call poll() more than once for a future. This problem can be mitigated by converting the future into a stream by using the into_stream() method or using the fuse() adapter, which adds a tiny bit of runtime overhead.
Tasks are usually executed/polled from using an executor such as the block_on() helper function. You can manually execute tasks by creating a task::Context and calling poll() directly from the task. As a general rule, it is recommended to not invoke poll() manually and to have an executor manage polling automatically.