* Rust errors can be equally unhelpful. Also, the error you posted is hands down awful. It doesn't tell you what went wrong, and it's excessively naive to rely on compiler to offer a correct fix in all but the most trivial cases. When errors happen, it's a consequence of an impasse, a logical contradiction: two mutually exclusive arguments have been made: a file was assumed to exist, but was also assumed not to exist -- this is what's at the core of the error. The idiotic error that Rust compiler gave you doesn't say what were the assumptions, it just, essentially, tells you "here's the error, deal with it".
* In Rust, you will have to deal with a lot of unnecessary errors. The language is designed to make its users create a host of auxiliary entities: results, options, futures, tasks and so on. Instead of dealing with the "interesting" domain objects, the user of the language is mired in the "intricate interplay" between objects she doesn't care about. This is, in general, a woe of languages with extensive type systems, but in Rust it's a woe on a whole new level. Every program becomes a Sisyphean struggle to wrangle through all those unnecessary objects to finally get to write the actual code. Interestingly though, there's a tendency in a lot of programmers to like solving these useless problems instead of dealing with the objectives of their program (often because those objectives are boring or because programmers don't understand them, or because they have no influence over them).
I don't follow your first point—the compiler is pointing out exactly what the problem is (the argument has the incorrect type) and then telling you what you likely wanted to do (borrow the String). What would you see as a more helpful error message in this case?
The compiler says "expected X, but found Y". I don't know how to interpret this: is the type of the thing underlined with "^^^" X or Y? "Expected" and "found" are just like "up" and "down" in space: they are meaningless if you don't know what the compiler expects (and why should it?).
What it needs to say is something along the lines of "a function f is defined with type X, but is given an argument of type Y": maybe the function should be defined differently, maybe the argument needs to change -- it's up to the programmer to decide.
I dunno, I feel like if you've used a compiler regularly, "expected X, but found Y" is a pretty common idiom/shorthand that people understand. Your wordier version of that feels unnecessary to me.
C is a low level language and deals with things close to the metal. It's probably not fun to write a large business app in barebones C but you having control over low level things makes other things possible and very fast too. Depending on the type of problem you have use the appropriate and favorite language.
Since it's underlining code you wrote, it must be "found" that is highlighted, not "expected". Much like up and down, gravity exists to ground all of us in the same direction.
I'm over here with TTS: Underlining in a terminal rarely translates to audio. It isn't the only consideration that needs to be made, when making things clear.
One thing that could help here is that the compiler is able to offer output in JSON, allowing you to format the messages however you'd like: https://doc.rust-lang.org/rustc/json.html
I'm not aware of an existing tool to produce blind-friendly output, but this would at least be a part of that!
The code tend to be loaded with primitives that express ownership semantics or error handling. Every time something changes (for instance, you want not just read but also modify values referenced by the iterator) you have to change code in many places (you will have to invoke 'as_mut' explicitly even if you're accessing your iterator through mutable ref). This could be attributed (partially) to the lack of function overload. People believe that overload is often abused so it shouldn't be present in the "modern" language. But in languages like C++ overload also helps with const correctness and move semantics. In C++ I don't have to invoke 'as_mut' to modify value referenced by the non-const iterator because dereferencing operator has const and non-const overloads.
Async Rust is on another level of complexity compared to anything I used. The lifetimes are often necessary and everything is warpped into mutliple layers, everything is Arc<Mutex<Box<*>>>.
> Rust errors can be equally unhelpful. Also, the error you posted is hands down awful. It doesn't tell you what went wrong, and it's excessively naive to rely on compiler to offer a correct fix in all but the most trivial cases
What? It tells the user exactly what's wrong
> Every program becomes a Sisyphean struggle to wrangle through all those unnecessary objects to finally get to write the actual code
That is the cost of non-nullable types and correctness. You still have to do the Sisyphean struggle in other programming languages, but without hints from the compiler.
* In Rust, you will have to deal with a lot of unnecessary errors. The language is designed to make its users create a host of auxiliary entities: results, options, futures, tasks and so on. Instead of dealing with the "interesting" domain objects, the user of the language is mired in the "intricate interplay" between objects she doesn't care about. This is, in general, a woe of languages with extensive type systems, but in Rust it's a woe on a whole new level. Every program becomes a Sisyphean struggle to wrangle through all those unnecessary objects to finally get to write the actual code. Interestingly though, there's a tendency in a lot of programmers to like solving these useless problems instead of dealing with the objectives of their program (often because those objectives are boring or because programmers don't understand them, or because they have no influence over them).