Lets take a look at disassembled code for this fragment:
for _ in 0..5_000_000 {
If you know that Rust's for is actually a syntax sugar for iterators, the result should no be so surprising:
mov dword ptr [rbp - 128], 0 // starting value
mov dword ptr [rbp - 124], 5000000 // ending value
mov rdi, qword ptr [rbp - 128]
call IntoIterator_into_iter
....
mov qword ptr [rbp - 264], rdi // iterator
Later Iterator::next() method will be called in a loop, returning Some(_) (actual value is ignored) or None, when iterator is done:
mov rdi, qword ptr [rbp - 264] // iterator
call Iterator_next
mov qword ptr [rbp - 152], rax // Option
mov qword ptr [rbp - 160], rax //
mov ecx, dword ptr [rbp - 160]
sub ecx, 1
...
je loop_body
jmp break
The call to Iterator::next() is costly, it is far from as simple as "inc eax", that is why sample is running so slow.
One interesting note is what happening when you compile with --release switch. Lets take a look at our iterator loop:
mov rax, 004C4B400000001h // hex 4C4B40 is 5000000 in decimal
mov [rsi], rax
Well, apparently compiler has outsmarted the programmer.
Whoa, that was fast!