If you are doing wrapping arithmetic a lot, there's a convenience wrapper (heh), Wrapping [3], so that if x and y have type Wrapping<i32>, then x + y always wraps (like calling wrapping_add on two i32). There's a Saturating<i32> too but it's nightly-only for now.
The only downside of Rust's approach is that if you have x + y on two plain integers, the language itself doesn't specify whether it will panic (and interrupt the execution of the program) or not. By default, debug builds will panic and release builds wraps (on the understanding that unintended overflows are always a bug, but catching this bug on release builds has some performance penalty, so it isn't caught by default on release), but you can control this by setting overflow-checks = true on the release profile on Cargo.toml [3]
I think future versions of Rust should set overflow-checks = true by default even on release builds, but right now it's only a matter of adding a single line on Cargo.toml to enable it, and almost every program should do it. With this on, x + y will panic on overflow if they are i32, will wrap if they are Wrapping<i32> and will saturate if they are Saturating<i32>
[0] https://doc.rust-lang.org/std/primitive.i32.html#method.wrap...
[1] https://doc.rust-lang.org/std/primitive.i32.html#method.satu...
[2] https://doc.rust-lang.org/std/primitive.i32.html#method.chec...
[3] https://doc.rust-lang.org/stable/std/num/struct.Wrapping.htm...
[4] https://doc.rust-lang.org/cargo/reference/profiles.html#over...
I do wish there was a(n optional) clippy lint to warn against non-explicit integer arithmetic operations though.
I chose ripgrep as a conservative choice, since most of the time should be I/O anyway.
I grepped for the word "Jesus" in a file that contained 100 copies of the king james bible (426MB of text). I ran it 100 times and averaged the results. Timed with /usr/bin/time.
--release without overflow checking was 0.0702 seconds on average.
--release with overflow checking was 0.0727 seconds, or about 3.5% slower
That's more than I expected for a program that's I/O bound.
> That's more than I expected for a program that's I/O bound.
Based on your description of your methodology and a reasonable assumption that you have more than ~426MB of available RAM, your benchmark is absolutely not I/O bound. Or at least, not bound by the speed of your disk. In your benchmark, your haystack is almost certainly entirely within memory and served from your operating system's page cache. It's unlikely you're hitting disk at all.
And even if you constructed your benchmark such that every search flushed your cache first and forced re-reading the file from disk, it may or may not be I/O bound. If your disk gives you 6 GB/s read speeds, you gotta do better than a traditional finite state machine to match that speed. (That is, probably SIMD.)
I should have been clearer. In my day job we are latency sensitive and we'll refer to excessively hitting main memory as being IO bound.
Try a literal that doesn't match or matches very rarely and you'll have a better shot at being limited by memory bandwidth. (But still maybe not.)
Try something like '\w+\s+\w+' instead. That will force the regex engine to run and you might see more impact from overflow checks. (But I'm just guessing.)
It also helps that, in practice, when writing idiomatic Rust you'll almost always be accessing array/vec members using an iterator. Direct indexing is uncommon, which means indexing bugs specifically are very uncommon. Of course that's not the only bug that overflows can cause, but it's a big one.
Not every bug can be prevented by the language (at compile time or runtime). You have to make some choices about which things are just in the programmer's hands, and I think Rust has struck a reasonable balance. Especially since the user can always choose differently in this case, and just compile their code with `overflow-checks = true`
Idk if branch prediction or whatever would have to be involved. Most architectures will already trap on divide by zero or things like that, and will set a condition code for int overflow. Making the condition code sticky like it is for IEEE floating point arithmetic would allow doing a software trap once a basic block finished. It is a shame that the current trendy architecture (Risc-V) goes the opposite way and has no condition codes at all.