I am not discouraging efforts to make compile times faster. However, I also see a lot of things that would really make Rust soar not being worked on, like syntax quality of life reworks that get complex under the hood being dropped, partially complete features with half baked PRs, IDE tooling and debugging support, interface-types and much of the momentum behind wasm, async traits and the sorely lacking async_std, etc. It seems like every time I dive into something moderately complex I start hitting compiler caveats with links to issues that have been open for 5 years and a bunch of comments like "what's the status of this can we please get this merged?". It can ever so slightly give one the impression that the rust community has decided that the language is mature and the only thing missing is faster compile times.
> "what's the status of this can we please get this merged?"
Having written Rust professionally for a number of years, this didn't happen too much. Where it did it was stuff like "yeah you need to Box the thing today", which... did not matter, we just did that and moved on.
> It can ever so slightly give one the impression that the rust community has decided that the language is mature and the only thing missing is faster compile times.
That is generally my feeling about Rust. There are a few areas where I'd like to see things get wrapped up (async traits, which are being actively worked on) but otherwise everything feels like a bonus. In terms of things that made Rust difficult to use, yeah, compile times were probably the number one.
I mean this is what you have to do to access variables from an async block:
let block = || {
let my_a = a.clone();
let my_b = b.clone();
let my_c = c.clone();
async move {
// use my_a, my_b, my_c
let value = ...
Ok<success::Type, error::Type>(value)
}
}
And you can't use `if let ... && let ...` (two lets for one if) because it doesn't desugar correctly.
And error handling and backtraces are a beautiful mess. Your signatures look like `Result<..., Box<dyn std::error::Error>>` unless you use `anyhow::Result` but then half the stuff implements std::error::Error but not Into<anyhow::Error> and you can't add the silly trait impl because of language limitations so you have to map_err everywhere.
It's not just "oh throw a box around it and you're good". It's ideas that were introduced to the language when there was lots of steam ultimately not making it to a fully polished state (maybe Moz layoffs are partly to blame IDK). Anyway I love Rust and we use it in production and have been for years, but I think there's still quite a bit to polish.
I just want to reiterate how ridiculous the async block example is. You have to add a wrapper block that captures the variables and clones them because there is no way to specify how you want variables to be captured and what works in a normal block does not work in an async block. Then, because of some other language/compiler limitations, the return type of the block can't be inferred so you have to specify it manually even when you use the block in a context where it ought be inferred trivially. All this adds up to the existence of the block syntax being more complicated than just defining a function to do the same thing (when blocks are supposed to be a convenience so that you don't need a bunch of function with context pointers taking pointers to other functions everywhere like you do in C). Which you'd happily do but wait, you're trying to curry a function for a specific use case where you pass it to an async iterator function, so the signature is fixed and you have to use a block to curry because rust doesn't support that either. So you end up with the above just to say "run this piece of code concurrently for each element in a list.
> I mean this is what you have to do to access variables from an async block:
I am clearly missing some context because that code is needlessly complex. Are you just trying to show that you have to clone values that you hold a reference to if you want to move them? Because yes, you do. But your example also needlessly borrows them in the outer closure.
It’s not needless. I explained it a bit in my previous comment but it’s not worth diving into in this forum. To get an idea, consider scenarios along the lines of transforming a list of data in parallel using an async collection/iterator where all the futures are spawned and joined within the local scope of a single function. Hit me up in the Rust Discord if you want to chat about it and go into more details. Otherwise all I can really say is that I have encountered a few scenarios where this is necessary but shouldn’t be with improvements to the compiler.
> - It's impossible to describe a type that "implements trait A and may or may not implement trait B"
So, specialization? Or something else? I haven't found a need for specialization. I remember when I came from C++ I had a hard time adjusting to "no specialization, no variadics" but idk I haven't missed it in years.
> - It's impossible to be generic over a trait (not a type that implements a trait, the trait itself)
It seems like a way to ask "Can this thing implement X and if so how?" from say the Any trait would be what you want here, I have no idea how hard that would be to deliver but I also don't see how the previous trait thing is relevant, like, why do we need to say up front that maybe we will care whether trait B is implemented?
You can if you know all of the possible types in advance. But if you want to expose this as an interface from a library that allows users to provide their own custom implementation then you need to use traits.
> It can ever so slightly give one the impression that the rust community has decided that the language is mature and the only thing missing is faster compile times.
Is not the case, is that the features are now good enough and compile times is the one major, big, sore point.
So, if you compare Rust to X you can make a very good case until you hit: