Change:
If compiling with we fail to remove bounds checks and panicking branches in code like this (godbolt ): Unfortunately, while all manage to remove the panic branches, does not, leading to a lot more code and significantly worse performance. Note that we do manage to remove it in cases where the function does less stuff -- it's removed entirely in simple examples that do nothing other than load the value from a slice. However, in real code, (perhaps after a layer of inlining) you tend to have a couple of these in a function, and this issue seems to crop up again[^1]. [^1]: Although not 100% reliably -- having trouble reproducing on godbolt, but it seems like it's happening in real-world code. Given that this pattern is more or less the recommended "safe" way to read a u32 out of a &[u8], this seems unfortunate to me.
I see two problems here: looks like the isn't inlined, and the check part of an unwrap probably always should be -- looking at the discriminant of a Result/Option is trivial, and would probably optimize out enough to be worth doing even in , with just the part of triggering the panic outlined. need better methods for getting arrays out of slices. For example, writing this with and () from , instead of the manual length check, makes it work.
Is there a reason to assume would optimize better than ? It isn't clear to me why this would be. Edit: Ah, there's a godbolt link. Note that this kind of indexing is extremely common in certain code (including commonly used functions like hash implementations), and isn't always as simple as the example (which is just first/last). It would be better if we could just do better in opt-level=z. Even opt-level=1 gets this right.
If there's an in both, then no, I'm not super-confident in it. But if it can be rephrased to not need the unwrap at all -- replacing the length check that needs deduping with the -- then yes, I'd expect it to work better. Have you perchance looked at ? I don't really know what the difference is between and for us or for LLVM, but it also wouldn't surprise me if is incredibly unwilling to inline stuff.
Yes -Os is fine (I mentioned as much above). The only two opt-levels with issues here are 0 (which is expected) and z (which is not). Note that in practice using if-let is viable for this version of the function, but it usually is not viable for this pattern.
is now , which appears to resolve this. It internally uses a call to , which is not inlined, to keep from exploding the size from inlining the discriminant check.

Discussion:
/// let x: Result<u32, &str> = Err("emergency failure"); /// x.unwrap(); // panics with `emergency failure` /// ``` <del> #[inline] </del> <ins> #[inline(always)] </ins> #[track_caller] #[stable(feature = "rust1", since = "1.0.0")] pub fn unwrap(self) -> T

<ins> // compile-flags: -C opt-level=z --edition=2021 // ignore-debug #![crate_type = "lib"] // From <https://github.com/rust-lang/rust/issues/115463> // CHECK-LABEL: @read_up_to_8( #[no_mangle] pub fn read_up_to_8(buf: &[u8]) -> u64 { // CHECK-NOT: unwrap_failed if buf.len() < 4 { // actual instance has more code. return 0; } let lo = u32::from_le_bytes(buf[..4].try_into().unwrap()) as u64; let hi = u32::from_le_bytes(buf[buf.len() - 4..][..4].try_into().unwrap()) as u64; lo | (hi << 8 * (buf.len() as u64 - 4)) } // CHECK-LABEL: @checking_unwrap_expectation( #[no_mangle] pub fn checking_unwrap_expectation(buf: &[u8]) -> &[u8; 4] { // CHECK: call void @_ZN4core6result13unwrap_failed17h buf.try_into().unwrap() } </ins>