> rest of the post is me trying to make sense of the tutorial on borrowing. It has fried my brain and negatively affected my skills in modern Rust, so be wary
I think that tutorial discouraged me from really getting into Rust
I was really hoping that there'd be movement on a comment without-boats made in https://without.boats/blog/why-async-rust/ to bring a pollster like API into the standard library.
Rust has very good reasons for not wanting to bless an executor by bringing it into the standard library. But most of those would be moot if pollster was brought in. It wouldn't stifle experimentation and refinement of other approaches because it's so limited in scope and useless to all but the simplest of use cases.
But it does in practice solve what many mislabel as the function coloring problem. Powerful rust libraries tend to be async because that's maximally useful. Many provide an alternate synchronous interface but they all do it differently and it forces selection of an executor even if the library wouldn't otherwise force such a selection. (Although to be clear such libraries do often depend on I/O in a manner that also forces a specific executor selection).
Pollster or similar in standard library would allow external crates to be async with essentially no impact on synchronous users.
The system Rust has is a lot better than that of Python or JavaScript. Cleanly separating construction from running/polling makes it a lot more predictable and easier to understand what's happening, and to conveniently compose things together using it.
I feel async is in a very good place now (apart from async trait :[ )
As a regular user who isn't developing libraries async is super simple to use. Your function is async = it must be .await and must be in an async runtime. Probably as simple and straightforward as possible. There are no super annoying anti-patterns to deal with.
The ecosystem being tokio centric is a little strange though
There are several ~~problems~~ subtleties that make usage of Rust async hindered IMHO.
- BoxFuture. It's used almost everywhere. It means there are no chances for heap elision optimization.
- Verbosity. Look at this BoxFuture definition: `BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;`. It's awful. I do understand what's Pin trait, what is Future trait, what's Send, lifetimes and dynamic dispatching. I *have to* know all these not obvious things just to operate with coroutines in my (possibly single threaded!) program =(
- No async drop and async trait in stdlib (fixed not so long ago)
I am *not* a hater of Rust async system. It's a little simpler and less tunable than in C++, but more complex than in Golang. Just I cannot say Rust's async approach is a good enough trade-off while a plethora of the decisions made in the design of the language are closest to the silver bullet.
Implementation-wise they're the same trick as C++, monomorphization.
Stylistically they're not very like either, however the effect is more like C++ because Rust idiomatically prefers to constrain functions not types so e.g. it's fine to talk about HashMap<f32, f32> a hypothetical hash table of floating point numbers mapped to other floating point numbers - even though we can't use such a type because if we try to insert into it we'll be told that insert requires its key parameter to implement Eq and Hash, which f32 doesn't because of NaN.
In both C++ and Java as I understand it these constraints live on the type not the functions associated with that type, although C++ does not have the same constraint here and is perfectly willing to try to make a hash table of floats... but where a constraint lives on a function in C++ it would behave similarly to Rust due to SFINAE - the function won't match so your diagnostics say there's no such function, probably worse diagnostics than Rust but that's par for the course in C++.
Other than the `|var|` syntax in closures, I can't think of a single way Rust looks like Ruby. I mean that seriously, there is almost no other similarities.
Fun trivia fact: this is basically the exact moment I first encountered Rust.
I’m also generally very glad at where it went from here. It took a tremendous amount of work from so many people to get there.
Hey, I remember your "Rust 1.0, ten years later" post. Good stuff!
I actually liked @T because you would pronounce it as "At T".
You could say "The address at T". Curious why people hated it, I might be missing something.
> rest of the post is me trying to make sense of the tutorial on borrowing. It has fried my brain and negatively affected my skills in modern Rust, so be wary
I think that tutorial discouraged me from really getting into Rust
> I’m happy with how Rust turned out.
I agree, with the possible exception of perplexing async stuff.
I was really hoping that there'd be movement on a comment without-boats made in https://without.boats/blog/why-async-rust/ to bring a pollster like API into the standard library.
Rust has very good reasons for not wanting to bless an executor by bringing it into the standard library. But most of those would be moot if pollster was brought in. It wouldn't stifle experimentation and refinement of other approaches because it's so limited in scope and useless to all but the simplest of use cases.
But it does in practice solve what many mislabel as the function coloring problem. Powerful rust libraries tend to be async because that's maximally useful. Many provide an alternate synchronous interface but they all do it differently and it forces selection of an executor even if the library wouldn't otherwise force such a selection. (Although to be clear such libraries do often depend on I/O in a manner that also forces a specific executor selection).
Pollster or similar in standard library would allow external crates to be async with essentially no impact on synchronous users.
The system Rust has is a lot better than that of Python or JavaScript. Cleanly separating construction from running/polling makes it a lot more predictable and easier to understand what's happening, and to conveniently compose things together using it.
I feel async is in a very good place now (apart from async trait :[ ) As a regular user who isn't developing libraries async is super simple to use. Your function is async = it must be .await and must be in an async runtime. Probably as simple and straightforward as possible. There are no super annoying anti-patterns to deal with.
The ecosystem being tokio centric is a little strange though
I write and use mostly async code, and I cannot for the life of me understand the async hate.
What do you want Rust to do differently?
What language does async right?
How did Rust not reach its async goals?
Rust even lets you choose the runtime you want. And most big libraries work with several runtimes.
I do write mostly async code, too.
There are several ~~problems~~ subtleties that make usage of Rust async hindered IMHO.
- BoxFuture. It's used almost everywhere. It means there are no chances for heap elision optimization.
- Verbosity. Look at this BoxFuture definition: `BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;`. It's awful. I do understand what's Pin trait, what is Future trait, what's Send, lifetimes and dynamic dispatching. I *have to* know all these not obvious things just to operate with coroutines in my (possibly single threaded!) program =(
- No async drop and async trait in stdlib (fixed not so long ago)
I am *not* a hater of Rust async system. It's a little simpler and less tunable than in C++, but more complex than in Golang. Just I cannot say Rust's async approach is a good enough trade-off while a plethora of the decisions made in the design of the language are closest to the silver bullet.
Very interesting to see the ML influences like ~ for unary minus, unscoped enums, mut on specific struct fields...
It seems like over time, a lot of that was replaced with C++-style syntax and semantics. Presumably to make the language appeal more to C++ devs
AFAICT the need to resolve unscoped enum values, combined with type inference, is one of the things that makes the Swift compiler so slow.
People wanted a new C++ and they made Rust into it.
Rust looks nothing like C++.
It looks like Ruby.
The generics look like Java.
How do you feel the generics "look like" Java ?
Implementation-wise they're the same trick as C++, monomorphization.
Stylistically they're not very like either, however the effect is more like C++ because Rust idiomatically prefers to constrain functions not types so e.g. it's fine to talk about HashMap<f32, f32> a hypothetical hash table of floating point numbers mapped to other floating point numbers - even though we can't use such a type because if we try to insert into it we'll be told that insert requires its key parameter to implement Eq and Hash, which f32 doesn't because of NaN.
In both C++ and Java as I understand it these constraints live on the type not the functions associated with that type, although C++ does not have the same constraint here and is perfectly willing to try to make a hash table of floats... but where a constraint lives on a function in C++ it would behave similarly to Rust due to SFINAE - the function won't match so your diagnostics say there's no such function, probably worse diagnostics than Rust but that's par for the course in C++.
Other than the `|var|` syntax in closures, I can't think of a single way Rust looks like Ruby. I mean that seriously, there is almost no other similarities.
Java Generics look like C++ templates and Rust generics act much more like C++ Templates than Java Generics.
Also, “new C++” refers less to syntax and more to role in the development ecosystem.