You could probably wrapp each T::from_le_stream
call in catch_unwind
. That said, for such code, using destructors is usually preferred over catch_unwind
, as it doesn’t need to interrupt and later resume the unwinding process.
A common approach here is to store handles that have access to stuff that needs clean-up in a struct
with a destructor, at some level, and possibly add into this struct additional information needed to do the clean-up correctly (you “only” need to make sure that at any point that can panic).
W.r.t. interacting with the iterator, I can imagine this can be difficult to keep within the loop, but outside of the loop, you can have a level of re-borrowed mutable. Access. Making a guard that holds a &mut [MaybeUninit<T>; SIZE]
field, then let the for
-loop re-borrow from that field. As additional information, you also need some usize
-type field that’s kept up-to-date appropriately to tell the destructor the amount of items that need to be destructed. Finally, you also need to consider how to “defuse” the guard on the successful path. (Either by making the Drop implementation effectively a no-op, e.g. setting the number-of-initialized-items back to zero, or by using something like mem::forget
.)
For inspiration, feel free to check out some code in the standard library for comparison, in implementing array::from_fn
and the unstable array::from_fn_mut
. (They have a Guard
struct that they use in two different places, which is why it’s not defined right next to – or even inside – the relevant function.)
Of course note that the any mechanism that handles your panic-case can easily handle your None
case as well, so you won’t need some code you already have.
A last follow-up question/challenge: What’s the intended behavior if some of the T
’s being dropped in the None
case has a panicing destructor? It’s a very niche question, but still interesting IMO. Very niche because really destructors are generally not actually supposed to panic. One could consider if not perhaps the remaining items in the array should still be dropped (giving more chance to a double-panic abort), or if one wants to leak them (risking that the overall program continues running with an avoidable data leak, which could accumulate over a long time).
If you do want to handle by dropping the remaining items, it’s also a challenge not to accidentally introduce any possibility for double-free to happen. (E.g. also the item whose destructor caused the panic must not be re-executed.) To make your life simpler, if you manage to call ptr::drop_in_place
, it automatically does the right thing of continuing to drop further items if earlier ones panicked, and it’s what the standard library uses. It’s also what arrayvec
uses and what Drop for Vec<…>
uses, and it has the additional benefit of skipping the whole potential loop reliably if the type in question doesn’t have any destruction logic (nor any of its fields).