The difference is whether you want to encode additional information for the caller in case of an error happening. In both cases all state is reverted.
If you panic (no matter through which means you emit the panic) then no output is emitted. Pretty logical as there is no way to return anything in case of a panic. Please note that the panic message is written to the debug buffer in case of a debug-contract and can be read by an RPC client during a dry run.
If you return a
Result::Err from your message then all state is reverted but the the caller decode this
Result::Err and act on it.
It is important to understand panics between cross contract calls are caught by default. The panic of a callee is returned as an error (
CalleeTrapped) to the caller and doesn't bubble up by default. So both errors are hand-able. Panics just don't convey any information to the caller about the reason for the error.