Migrate to (std::)futures 0.3
Futures 0.2 made some breaking changes so the examples here won't compile.
It would be awesome for supporting futures 0.2.
Hey, this would improve some dependencies, but I have no real issues with the old version and will wait for the 0.3 release. The 0.2 release should not be "used too heavily" https://www.reddit.com/r/rust/comments/8ac85w/futures_02_is_here/dwxm9on/ But we can discuss some issues here or migration strategies to the new version of the Future crate.
I think I should update this issue to "Migrate to/support for the stabilized async-await syntax or std::future+FuturesExt".
EDIT: add missing period mark.
Didn't expected they would need so long for futures 0.3. For now this is blocked on https://github.com/rust-lang-nursery/futures-rs/milestone/2 after finalizing and stabilizing the syntax we can switch to the new std::futures. From an API standpoint this will give us no advantages though.
The async-await syntax is a nice addition, but don't know yet if we need it. There are no complex async routines which can't be expressed in a functional way and IMHO the functional style is more clean and easier to read than async-await.
Hello, I'm really bad at explaining things, but I will try to throw in my two cents and showcase the one issue the async/await syntax (and therefore migrating to std futures) would solve.
Since I'm bad at putting this into words, I'll show two resources:
- https://tokio.rs/docs/futures/combinators/#returning-from-multiple-branches
This is a fundamentally unsolvable issue with Tokio; functions returning
impl Futuremust still have only one return type. They provide a workaround withEither, but that gets ugly real quick.
Easily reproducible example with telebot:
Work with get_stream(None) to get the updates stream and process incoming messages, answering them with a new message, as well as incoming inline queries, answering those as well.
Now you need Either already; but what happens when, say, we want to have a path that sends an image?
Yet another return type is required, resulting in the mentioned Either<Either<A, B>, C> type, so the not-so-beautiful Either::B(Either::A(... declarations start (or a custom enum needs to be added, which I'd need to manually implement Future for, increasing the required boilerplate). This gets even uglier with more options.
With .await, the story becomes a lot easier:
Instead of returning Either::A(bot.message(...).send()), Either::B(Either::A(bot.answer_inline_query(...).send())) and Either::B(Either::B(bot.photo(...).files(...).send())), you can now write something like
let result = bot.message(...).send().await;
// Error handling logic
Ok(())
and repeat that for all three or maybe even more branches, without going into Either::B(Either::B(Either::B(Either::B(...)))).
- https://gitlab.com/Follpvosten/godfishbot_rust/blob/master/src/main.rs#L207 My personal example
Here, I use the result of the send() future to update a command's externally stored state if necessary.
The only way of doing that is my using combinators, so that complicates it by requiring a move etc.
With .await, I could e.g. assign the result of the send() futures to the same variable (because they all return Message) by just letting them be the value of the whole match block, and then do the common code (the post-send processing) just once. (There's probably other things I could do better too, but that's the first one that comes to mind.)
One more thing: I'm currently thinking of changing CmdResult.data, which currently can be an Image, Voice or Text, to optionally be a future resolving to one of those options (mostly because querying external APIs should definitely be done async). Implementing that with .await would be trivial, while I can't even think of how to do it with combinators right now.
I hope my arguments weren't too badly worded to be understood and I got the point across. If there are ways to easily solve these issues in a functional style, I can't think of them, so they're probably not too obvious. In any case, I hope this was a valuable contribution. Thanks!
Okay, so I'm still learning and improving all the time, and I've refactored my code a fair bit by now; remembering that I can chain multiple and_then combinators helped quite a bit there, lol.
You can now pretty much forget my second point as it's not relevant anymore; I'll leave it in there, as the link still shows a real-life example of the not-too-good-looking Either::B(Either:A(...)).