telebot
telebot copied to clipboard
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 Future
must 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(...))
.