5 ms·
If you are interested in rust and want a very simple project to try it on: I made a bot library for https://turntable.fm/ https://turntable.fm/ in javascript -
by alain_gilbert 5y ago
If you are interested in rust and want a very simple project to try it on:
I made a bot library for https://turntable.fm/ https://turntable.fm/ in javascript -> https://github.com/alaingilbert/Turntable-API https://github.com/alaingilbert/Turntable-API
Then I rebuilt it in go -> https://github.com/alaingilbert/ttapi https://github.com/alaingilbert/ttapi
Then I tried to write it in rust, but the pattern isn't really good with the borrow checker, I cannot make this work -> https://github.com/alaingilbert/rust-ttapi/blob/47282b7a334d947ccc38cf7e9179fae2cb632b96/src/main.rs#L342 https://github.com/alaingilbert/rust-ttapi/blob/47282b7a334d...
So if someone can find a good pattern to use for this type of library, I would gladly want to learn.
Pull requests are welcome, I'm also always on discord if you want to chat.
- rav 5y agoYou cannot access "bot" inside bot.on_speak(|| { ... }) because Rust does not know that the "bot" variable stays alive for whenever the callback is called. In this situation I would expect "bot" to be stored in an Arc, and then you would move a clone of the Arc into the callback: let bot = Arc::new(bot); bot.on_speak({ let bot = bot.clone(); move |evt: SpeakEvt| { ... }}); Edit to add: Alternatively, the bot could pass a mutable reference to itself to the callback when it is invoked. bot.on_speak(|bot, evt: SpeakEvt| { ... })
- alain_gilbert 5y agoeasier said than done though. I tried the second option (calling callback with a mut reference to self) but I couldn't make it all work. I think I also had issues with "async" in a closure that could not be done. Anyway, if you can make it work, I'll study your code religiously !
- masklinn 5y ago> I think I also had issues with "async" in a closure that could not be done. Yes, async closures are not currently stable, which is a huge pain in the ass: you have to use a regular closure with an `async` block inside, but said async block will usually need to be an `async move` block, which requires more contorsions because you can't just use stuff the closure owns as that'd make said closure FnOnce (the closure's item is moved into the `async move` block meaning the closure is destroyed). This is usually solved by putting the item behind an `Arc` (if it's too costly to be directly copied, or if you want / need to mutate it), this way you can `clone()` the arc so the async move block has its own "copy". Alternatively, have the closure immediately and only call into an async function or method. I can't wait for async closures, though, I'm sure others have their own bugbears but that's by far my biggest pain in the ass when using async rust.
- rav 5y agoHere you go: https://github.com/alaingilbert/rust-ttapi/pull/1 https://github.com/alaingilbert/rust-ttapi/pull/1
- csomar 5y agoThis is not a borrow checker issue, if I understood correctly. You are using Rust callbacks, the same you are using JavaScript ones. The problem is that you are not accepting a generic callback in "on_speak" but a particular type that you define: fn(SpeakEvt). If you try to capture variables inside your callback, you should use define an FnOnce type. But this is just the start of your problems. To use Async, your Fn type should now also return a Future since these async tags will infect all your functions and their returns. tl;dr: I'll start over and use Channels.
- alain_gilbert 5y agoUnfortunately, it is impossible to have secure websocket (wss) without using "async". (as far as I know)
- Nullabillity 5y agoYeah, callbacks don't really play well with async Rust at the moment, due to those borrow issues. One way to work around it is to define your Bot struct as a Stream of Events (an enum for all of your current callbacks). It's a bit of boilerplate to define, but then your client code can look something like this: while Some(event) = bot.try_next().await? { match event { Event::Speak { text, .. } => { bot.speak(&text).await?; } _ => {} } }