There's one significant thing that's in C that's not in Rust: alloca. If you need that, well, we've talked about it, but haven't accepted a design, and some people never want to gain it directly.
EDIT: another comment pointed out goto; I have no idea what the performance implications are but I’ll have to add that to my list of “stuff C has that Rust doesn’t.”
Sometimes, you need nightly-only features. This means that, in some sense, today's Rust is slower, but tomorrow's Rust may not be.
Sometimes, "equivalence” is the issue. You can turn off bounds checking for array access, for example, but most Rust code doesn't, for hopefully good reason. Sometimes those checks are elided, and so it's exactly the same. Sometimes they're not. Sometimes they're not and that inhibits other optimizations. Is unsafe Rust "equivalent" to the C? Or does that not count? (Unsafe Rust is not always faster than Safe Rust...)
At the theory level, I can't think of anything off the top of my head that would inherently make Rust slower than C, generally speaking. There's also some degree of argument that in theory, Rust should be faster than C, thanks to how much more we know about aliasing, etc. That's a whole other can of worms...
This is something I've personally thought about a lot and I have no idea how I'd actually want it implemented either on the backend or in the syntax, but I think it'd be useful to have some way to say "I want these bound checks to be elided, if they can't be then make that an error". Similar to how you'd decorate a pure function in other languages to say that it can't cause side effects or depend on side effects for what it's doing.
I think that's probably a high level ask that's probably significantly more difficult that it seems initially too.
Edit: Looks like there's been an RFC in the past to add it to rust, but it got punted because the const generics stuff went in first and they wanted to see how that would shake out before trying to fully tackle this. https://github.com/ticki/rfcs/blob/pi-types-ext-2/text/0000-...
On that front, one interesting thought would be to reimplement the CPython bytecode interpreter in Rust and see if you can match the performance of C using computed goto.
In principle, there's no fundamental reason Rust couldn't optimize "loop around match" exactly the same way, without needing the computed goto. (For that matter, so could C.) Doing that would help cases like this.
For safe Rust, sure, there are limitations: you can't turn off bounds checks, for instance. But all Rust is partially unsafe Rust, because the standard library uses unsafe code ⃰. So it's a fuzzy distinction.
⃰ ⃰You wouldn't want it any other way. Implementing low-level functionality as unsafe code allows us to write code, not compiler-code-that-generates-code.
In pseudocode
Goto = Start;
While(Goto!=End) {
Match(Goto) {
...
}
}
It’s a pretty basic obfuscation technique.
Compilers will happily unroll this leaving you with an irreducible CFG.* In C++, using goto: https://godbolt.org/z/YqCMbU
* In Rust, with relooped CFG: https://godbolt.org/z/IIXtKo
The compiler was obviously not able to unroll the relooped code into the original CFG.
A specific example: in principle, the compiler could see the exact target of every continue in the Rust code, so the continue on line 34 could go directly to line 27 (or, better, as in C++ the basic blocks could just be laid out adjacently), but the compiler does not actually do this and there are a bunch of unnecessary tests on the path between those two lines.
This doesn't work in general, and is ... impossible to maintain, but it's not an unreasonable approach for generated code.