| 437 | |
| 438 | #[allow(clippy::as_conversions)] |
| 439 | fn visit_mut_pre_post<F1, F2>(&mut self, pre: &mut F1, post: &mut F2) |
| 440 | where |
| 441 | F1: FnMut(&mut Self) -> Option<Vec<&mut Self>>, |
| 442 | F2: FnMut(&mut Self), |
| 443 | { |
| 444 | // This code uses `unsafe`. The core safety argument is that: |
| 445 | // |
| 446 | // - `children_mut()` produces disjoint children |
| 447 | // - no aliasing means each `Enter` is processed separately, and we `Leave` each node exactly once |
| 448 | // - even if `pre` modifies the pointer, we retake it before computing children |
| 449 | // |
| 450 | // Put another way, our `stack` mirrors the function call stack, which allows multiple `&mut` refs at once, |
| 451 | // since only one stack frame can be active at a time. |
| 452 | |
| 453 | use VisitMutAction::*; |
| 454 | let mut stack = vec![Enter(self as *mut T)]; |
| 455 | while let Some(action) = stack.pop() { |
| 456 | match action { |
| 457 | Enter(ptr) => { |
| 458 | let elt = unsafe { &mut *ptr }; |
| 459 | stack.push(Leave(ptr)); |
| 460 | |
| 461 | if let Some(children) = pre(elt) { |
| 462 | for child in children.into_iter().rev() { |
| 463 | stack.push(Enter(child)); |
| 464 | } |
| 465 | } else { |
| 466 | let elt = unsafe { &mut *ptr }; |
| 467 | for child in elt.children_mut().rev() { |
| 468 | stack.push(Enter(child)); |
| 469 | } |
| 470 | } |
| 471 | } |
| 472 | Leave(ptr) => { |
| 473 | post(unsafe { &mut *ptr }); |
| 474 | } |
| 475 | } |
| 476 | } |
| 477 | } |
| 478 | } |
| 479 | |
| 480 | #[cfg(test)] |