Here's an implementation that is equivalent to the modern C++ example (playground link:
https://play.rust-lang.org/?version=stable&mode=release&edit...):
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".