-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
confusion specific to JavaScript #140
Comments
Possible example: when you call an async function in JS, it begins executing immediately, and executes concurrently, whereas Rust futures are lazy. |
Some other topics that might be worth covering here:
|
Hi y'all! ❤️ Coming from JS here are some things that made an impression to me as a beginner. Runtimes🔁 I didn't know how to imagine what a runtime until someone said it's like a glorified while loop and that the special macro re-writes my code into something else. Errors⛔️ Handling errors in Tide was weird, for example if I wanted to upload an audio file, I'd have to do some parsing and writing to the filesystem. Usually I'd slap a question mark at the end of each function that returns a result, but when I wanted to turn those into meaningful errors I got confused. I still don't know how to properly do error handling so I use dyn Error result types everywhere if many errors can happen in a function 😬. Although the compiler catches so many things that I rarely need a debugger so there's that. References and ownershipThese took the longest to figure out. My dad and I have been coding an audio player server for fun every Saturday (🥳) for a while and it tripped us up when we wanted to do ƒancy hot-reloading. It was very rewarding to learn that you can shoo off the borrow-checker by lighting a candle and burning some incence in front of a portrait of Esteban, or just using .clone() everywhere. Oh, and passing closures around is really hard. Love these
|
Some things that I would like to be improved in Rust, that I find useful in JS:
|
There's a subtle difference in JS between returning Promises or awaiting them before returning. Suppose async function returnSomething() {
return createPromise();
} async function returnSomething() {
return await createPromise();
} The two snippets lead to different results. This article explains the difference in detail. It can be very confusing even for experienced JS devs. Rust works differently, which can bring even more confusion. For example, the code below panics because I don't use futures::executor::block_on;
async fn return_async_1() -> usize {
return 1;
}
async fn return_call_async() -> usize {
// return return_number().await;
return return_number();
}
fn main() {
let s = block_on(return_call_async());
print!("{}", &s);
} The provided error message is very uninformative and could be improved (e.g. explain why I can't do it this way? Is it possible by returning some other type? Should I return
|
When learning Rust a lot of emphasis is, rightly, placed on ownership/references and mutability. However, there are some marked differences in data types that are easily missed. The various integer types/sizes is one. The other is vectors versus arrays. It's all too easy to assume arrays are what you want because of the JS name similarity. |
Cancellation:In JavaScript, cancellation is handled with AbortControllers + AbortSignals. In Rust it is handled by not calling poll and the Drop trait. In JavaScript some APIs don't take an abort signal and you end up needing to wrap them: https://gist.github.com/jakearchibald/070c108c65e6db14db43d90d1c3a0305 In Rust, all Futures can be cancelled. In JavaScript cancellation is propagated via exception from bottom to top (by bubbling exception) instead of being propagated from top to bottom (by futures dropping their children) as it is in Rust. Maybe it would help JavaScript users to have examples that show converting cancellation patterns in JS into Rust. async function do_the_thing(data, signal) {
const thing = create_a_thing(data);
try {
const res = await use_thing(thing, signal);
return res + 2;
} finally {
thing.cleanup_the_thing();
}
} |
I think a lot of people coming from js look for Here was my thrashing attempt at understanding how to make outbound http requests with bounded concurrency: https://twitter.com/jacobrothstein/status/1212892911546683394, and critically for this wg, I ended up giving up on async and instead used rayon for this project: https://twitter.com/jacobrothstein/status/1213248527079313414 A little analysis: The futures crate has a lot of good ideas, but also some less-than-useful ones, and very few of them are documented in a didactic/tutorial manner, and the naming could stand to be improved for discoverability. There's no "so you want to iterate over this Async-std and tokio have different approaches than those in futures, and it's not obvious why/when you need to reach for the futures crate (or, even more confusingly, futures-util / futures-lite, which also have differences). It's especially not obvious that a vec of futures needs to be turned into a stream in order to do async stuff with it, since in javascript there are lots of apis that operate on an array of promises without making it into a special thing. I'm much more familiar with async-std than tokio, but I don't believe async-std currently has any equivalent for FuturesUnordered or BufferUnordered, which leads people to try other solutions that don't work as well, like trying to fold a vec of futures into one giant |
Some things that I was struggling to understand after working with JavaScript for a long time:
|
This sums it up for me: https://github.com/rhmoller/blobs-and-bullets/blob/master/src/engine/image_future.rs I just wanted to do the equivalent to this. const loadImage = src => new Promise((resolve, reject) => {
const img = new Image();
img.onload = resolve;
img.onerror = reject;
img.src = src;
}) It took me forever to figure out. And now that I look at it a year later I can't remember how it works. (I haven't been writing Rust since) |
BTW the above is not to dunk on Rust. I overall like the language and would like to pick it up again sometime. |
I recently bounced off rust async after thinking about using it for the 4th or 5th time over the years. HistoryI'm somewhere between Alan and Barbara, but I remember before I was Alan and what pain I felt then. When Promises showed up in JS, I largely avoided them, hooked in via my well worn callbacks. This was rooted in a few concerns, some were performance reasons, but mostly it was interoperability with other code I had. In these days, using My early encounters with JS promises were learning a lot of interesting rules about how if you As I dove into that world of using Promises, I found there was a few different gotchas when converting callback code. Subtleties like how The ecosystem has come a long way in those years since Promises landed in Node.js, I now write Promise first code for 99% of my JS at work. If a library has a promise interaction I'll use it over a callback one for most things. I've been a rustacean since just before 1.0, was lucky enough to attend RustCamp, and generally been hacking in rust for side projects for years and years now. I've written a lot of CLIs, a few art projects with my own rendering engine, and my own game engines. So I'm not coming at Rust fresh, I remember try!() and how often I used to have to specify lifetimes on everything. I've even taught local workshops and given talks about Rust. Why I bounce offI get vibes of the early JS promise when I start diving into Rust async. Also the amount I have to learn to start modifying my code confidently feels massive. Here's an ordered list, which is akin to some others on here
My process that leads me to rust async and maybe part of why I bounce off:
I wrote this as a stream of consciousness so please forgive some transposed words and weird phrasing where my brain jumped about. I would like to have a grasp on rust async, maybe someday I'll have picked up enough tangentially and the ecosystem will come far enough that it does't seem like a huge lift to just try it out in a project. Edit:
|
Yet another story: I'm coming from a Java background with some familarity with C/C++, but am working with TypeScript/Node.js for >3 years now on a daily basis. What really bugs me in Node.js is the thing that creating a Promise will automatically add it to the Event-Loop. This means that every time you do that (either explicitly with That's why I absolutely love Rust "explicit polling" approach. Coming from the problem mentioned above in Node.js lead me to always explicitly calling Due to this approach I did not understand the recent fuzz about "
|
|
Brief summary
Alan is accustomed to JavaScript promises. He has design patterns in mind that don't work in Rust; he also gets confused by specific things around Rust futures. What are they?
Optional details
The text was updated successfully, but these errors were encountered: