The issue is that many libraries (including std/stl) tend to produce long compile times.
I feel that in modern C++ modern style ”trust the compiler” often means ”leave the programmer out of the loop”
I really, really would hate to have to try to debug the ranges example, for example.
use std::time::SystemTime;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let t0 = SystemTime::now();
let triples = (1..).flat_map(|z|
(1..=z).flat_map(move |x|
(x..=z).filter_map(move |y|
if x * x + y * y == z * z {
Some((x, y, z))
} else {
None
}
)
)
);
for triple in triples.take(100) {
println!("{:?}", triple);
}
let elapsed = t0.elapsed()?;
println!(
"{}",
elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 * 1e-9
);
Ok(())
}
It takes about .376s to compile in debug mode, about 0.491s in release mode. This was after warming caches; the first run took about 2.071s, of which a large portion was probably loading the compiler and standard library.In debug mode, it runs in 0.047586s, in release mode, 0.002304.
I was pleasantly surprised to see this is actually better on the build times and debug build performance, given that those are two often cited sore points in Rust.
I would also say that while this is a little bit noisy, it is more readable than the proposed C++20 version. "1.." looks more like a range starting at 1 than "iota(1)" does. There's an actual "if" statement in there.
In fact, while Rust doesn't officially have generator/coroutine support yet, it is available in nightly builds for the implementation of async/await. If you use the nightly compiler, enable the unstable features, and wrap an adapter type around Generator that implements Iterator, you can write:
let triples = IterGen(||
for z in 1.. {
for x in 1..=z {
for y in x..=z {
if x*x + y*y == z*z {
yield (x, y, z)
}
}
}
}
);
It's expected that at some point in the coming year or so async/await, and probably the underlying Generator feature, will be stabilized, and I presume the standard library will implement the Iterator trait for appropriate types of Generators (though there is some bikeshedding about the details of that).Rust doesn't have generators yet, so this is only really fair to compare to the generator/co_yield example in C++ (which is also proposed but not yet standardized). I'd say Rust's syntax compares favorably to that example; the ranges are more clear than the traditional C-style "for" loops, there's native tuple syntax, and it's just be "yield" instead of "co_yield".
While on C++ I can take advantage of incremental compilation and linking, while using binary libraries for all dependencies, with cargo I have always to build the whole world from scratch minus standard library.
It still doesn't do incremental linking, and cargo still builds everything from scratch without any support for binary libraries.
Additionally, it is not like every C++ library is full of meta-programming templates.
Finally, there are other languages with library "metadata" and generic data types, which can easily beat Rust compile times like Delphi, Eiffel and Ada.
And are you sure GCC's Ada has better compile times? My sense is that so few people use Ada at scale that we don't really know.
Rust is definitely further along the scale towards the end of "giving more to the compiler so it takes longer". I usually like the trade. Most of the time.
While I do appreciate build speed improvements, saying that it's better than it was isn't a super high bar in terms of user experience.
I love Rust, I really do, but it's still incredibly slow. I'm excited for any progress it makes on this front.
> In many ways the '++' in 'C++' comes from giving the compiler more information to work with.
This is definitely not the origin of the '++'. I believe the '++' name was literally a play about "one better than C."
At least, that's how I've usually understood it. And the example you mention seems like a complaint with C++20's Ranges in particular. I guess I'd fall into the, "but can't you just ignore the parts you don't like?" camp.
This post is written in the context of game engines, but I see a lot of the same 'OG C' arguments made when people in embedded development resist giving C++ a try.
We're all writing resource-constrained applications that work on tight time budgets, and none of us feel comfortable throwing up our hands and 'trusting the compiler' in that sort of atmosphere. But C isn't getting any younger, and it's hard to do polymorphism without getting hack-y.
Now, who's turn is it to advocate Rust?