| Rust doesn't have to output more code than a C compiler. But it tends to because most rust programs are stuffed full of bounds checks. And bounds checks aren't small. As well as the conditional itself, every bounds check also includes: - A custom panic message (so you know which line of code crashed) - Some monomorphized formatting code to output that message - The infrastructure to generate a stack trace after a panic - Logic to free all the allocated objects all the way up the stack If you compile this 1 line function: pub fn read_arr(arr: &[usize], i: usize) -> usize {
arr[i] // (equivalent to 'return arr[i];')
}
... You produce 20 hairy lines of assembler: https://rust.godbolt.org/z/dhz34KEvjIn contrast, the equivalent C function is this rust code: pub fn read_arr_unchecked(arr: &[usize], i: usize) -> usize {
unsafe { *arr.get_unchecked(i) }
}
And predictably, the result is this gem - identical to what the C compiler outputs: example::read_arr_unchecked:
mov rax, qword ptr [rdi + 8*rdx]
ret
But nobody writes rust code like that (for good reason). You can get a lot of the way there by leaning heavily using rust's iterator types and such. But its really difficult to learn what patterns will make the rust compiler lose its mind. There's no feedback on this at compile time, at all. |
[1]: https://github.com/AccessKit/accesskit