When writing unsafe Rust, you should follow these principles:
A function with an unsafe block (regardless of whether it is itself unsafe or not) has the responsibility to be sound: to ensure that if its safety conditions are met, calling it does not cause UB.
A function with zero safety conditions does not need to be marked unsafe, and usually should be safe.
In this case, the function can be safe because assert!(mid <= len) ensures that no out-of-bounds access can occur.
If this function constructed overlapping mutable references, that would be a bug in the function. “This function may have bugs” is not a reason to mark it unsafe, because unsafe fn is for giving additional responsibilities to the caller. There is no way for the caller to fix bugs.
As a meta-point here, this can't be the rule in any reasonable way: it would mean that almost everything ends up unsafe, eliminating the whole point of using Rust.
The zen of rust is "we wrap up unsafe things into safe interfaces", using the type system to allow doing that with minimal (if any) overhead -- that's how you can call Vec::push in safe code even though it's doing operations under the hood that are individually extremely unsafe, for example.
So unsafe is used by developers to tell the user "Use it as we say, and it's safe". And the user decides to trust or not. (Also within our own library.)
The safe wrapper instead means they decided it's safe, and we can use it as normal code.
But it's not taken as a big problem that a user may inadvertently run a function which is wrapping unsafe code, which could be costly.
In the latter case, for any concerns, a user could use the geiger crate, or read the source.
As an example of the other way around, I can present this:
The function does not contain any unsafe { } block or run unsafe code.
It is there to remind the user to uphold the invariants that the struct encapsulates and that they need to reason about the validity of the header for the given payload when constructing a new frame like this.
That means that in a world where you intuition is correct safe languages don't exist: all ”safe” languages are built on top of “unsafe” core, because only “unsafe” core may actually read the keyboard, change pictures on the screen and so on.
Usually that core is written in C, but lives in the same process. Sometimes it could be part of the OS, like in Symbolics offers or Microsoft experiments. But it's always there. It's unavoidable.
What Rust does is simple thing: it just admits the reality. Instead of forcing you to learn a different language to be able to use “unsafe” core that's always there and couldn't be eliminated… it gives you unsafe.
But the core idea is the exact same as in any other “safe” language. The only differentce is that you may do magic without using C or any other “special” language, just Rust with a couple of special additions.