error: could not compile `the_project`.
And nothing else. No hint of what the problem is.It then took me a long time to try to comment out code here and there to find out what was causing the error.
When I finally found out the problem, I took the time to fill a bug report. I don't know if anyone looked at it. https://github.com/rust-lang/rust/issues/73026
I wouldn't call that lack of output representative of the experience of using rustc though, if I'm allowed to let my pride on my work flare up a bit.
Complicated error messages are clearly no a common occurrence unless you rely heavily on meta-programming tricks, and that is not common in Rust, unlike C++.
Saying in it inherited them, just proves you probably didn't use either.
While compilation times are longer than say Go, they definitely never felt too long, compared to say Java.
But your comment around errors takes the cake. Like maybe, if you use some combination of macros expansion and traits it could get confused.
But Rust errors are on par with Elm. They show the line, they show what went wrong and how to fix. This is a far cry from C++, where using even a bit of templates, results in unintelligible mess.
Woah... now I am the one who have to ask: what the hell are you on about??
I work on Rust and Java projects, if you exclude running tests, Java compiles very very fast compared to pretty much any language... Rust is a lot slower to compile even on the much smaller projects I've worked on... in this post, a very small project (I think it's like 16,000 LoC, it's mentioned in the beginning) was taking over a minute even on hot builds... To get a build to take this long in Java you would need to have several million LoC! Even if you don't use something like Gradle to cache compilation units and just compile everything from scratch with javac.
> I don't love how the dependency graph looks
After adding this complexity to the project, yes, some of the crates are building in ~3s, if they are the only ones that needed to be rebuilt.
Meanwhile, in a Java project there is none of this artificial crate splitting. Sure, Java projects have other nonsense, but they don't force you to split up projects into tiny pieces to get decent compile times.
Things that have like 420+ dependencies (spring boot + some other requirements). I timed the maven clean compile. And it takes around 2min.
In my experience C# compiles faster, but I still agree with you on all points.
But I've got to say I'm super jealous of the level of introspection the rust compiler allows here. I'd definitely be willing to change a few C# coding patterns in code bases I maintain to benefit build times, but as is, that's a really painful trial and error process. And sometimes you get lucky finding the root cause, but if you don't... well, it's not a great experience.
As to "How to fix it" however if you have lifetime confusion the error reported by the borrow checker probably isn't going to suggest refactoring your code with a design that makes sense even though that's likely the correct solution. It would be cute if the Rust compiler said "I think you want a Boxed collection of strings here to use the algorithm you're attempting, change how this is represented in all the related structs too, here's how:" but that's unrealistic and, more importantly, no other language is doing a better job here.
While Rust makes the errors themselves look easy, the right suggestion is context sensitive anyway. In my AoC solutions, unwrap() was the correct answer anywhere the problem is "I have an Option or a Result and I wish that I didn't". Because it's AoC if I'm wrong to be so cavalier I will eat a panic immediately, and I don't plan to maintain this stuff. In a real project I probably want to explicitly handle None and Error in some places, and pass on Errors in other places, even when I don't I should write an expect() in case I'm maintaining this years from now.
More likely in lifetime confusion scenarios the equivalent C++ compiles but just has mysterious crashes or bugs and the equivalent Java has runtime Null reference errors, Concurrency errors, a truly enormous memory leak, data races, other mysterious mutation bugs, or all of the above. I'll take a compile time error from the borrow checker thanks.
I think we could provide such messages in some cases. :)
For example, if you have an impl Trait return type and have multiple returned values of different types, we suggest using Box<dyn Trait> when valid. It's just a lot of work to get those in place. You're effectively implementing a language that does those things automatically, plus all the work to print out the actual diagnostic.