So there's no dangling ptr but it may points to invalid &str, right? Note that references must always points valid values. In this case you still can't read the ptr since reading invalid &str is UB even if underlying str will never accessed.
So it is still unsafe. Well, what about a *mut &str &str, is it safe to drop_in_place(*mut &str &str) if the &str points invalid memory but itself is valid?
You've asked why drop_in_place(&T) is unsafe. The answer is it's safe. Since &T is a reference it always points to valid T. and the T here is Copy so dropping is no-op. Safe Rust normally doesn't allows to access invalid values.
It may be confusing since we're using the term "safe" in 2 different meanings - the absence of the keyword unsafe in code and the validity of the code. For this reason in the safety context it's common to call the latter "sound" or "soundness". Sound code may contains some unsafe code but it correctly follows all the rules.
Is it correct I conclude as that:
drop_in_place(&&str) is unsafe because it equals read(&&str) -> &str, but &str is invalid because str is invalid; but drop_in_place(&&&str) is safe because the returning value us &&str, and there is no recursively drop_in_place(&&str)
Specially, I am not very sure about drop_in_place(&&str) is unsafe, because
This is semantically equivalent
The real code is a lang builtin
#[stable(feature = "drop_in_place", since = "1.8.0")]
#[lang = "drop_in_place"]
#[allow(unconditional_recursion)]
pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
// Code here does not matter - this is replaced by the
// real drop glue by the compiler.
// SAFETY: see comment above
unsafe { drop_in_place(to_drop) }
}
&T is invalid if it points to invalid T. So &str is invalid if it points to invalid str, and &&str is invalid if it points to invalid &str and so on. Valid str consists of 0 or more initialized bytes which also is well-formed UTF-8 sequence.