telebot icon indicating copy to clipboard operation
telebot copied to clipboard

Migrate to (std::)futures 0.3

Open RedL0tus opened this issue 6 years ago • 5 comments

Futures 0.2 made some breaking changes so the examples here won't compile.

It would be awesome for supporting futures 0.2.

RedL0tus avatar Apr 07 '18 02:04 RedL0tus

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.

bytesnake avatar Apr 09 '18 05:04 bytesnake

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.

RedL0tus avatar Jun 13 '19 06:06 RedL0tus

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.

bytesnake avatar Jun 13 '19 16:06 bytesnake

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:

  1. 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 with Either, 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(...)))).

  1. 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!

Follpvosten avatar Jul 13 '19 22:07 Follpvosten

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(...)).

Follpvosten avatar Jul 17 '19 17:07 Follpvosten