0

This code takes some of the ideas of replace_with and extends it to structures, allowing a function to be applied to a container in a way that cleans up after itself if there's a panic. Unlike running replace_with on all the elements, one doesn't need a default value for the element types, only the container type.

I can't get the lifetimes right. If I replace TStruct with Vec<T> everything works fine without lifetime annotations, it's only when I try to generalise the function that I get lifetime issues. What lifetimes do I need to put in here to satisfy the borrow checker?

fn inplace_map<T, TStruct: IntoIterator<Item = T> + Default>(
    f: impl Fn(T) -> T,
    mut x: TStruct,
) -> TStruct
where
    &mut TStruct: IntoIterator<Item = &mut T>,
    &TStruct: IntoIterator<Item = &T>,
{
    let x_ptr: *mut TStruct = &mut x;
    for item_ref in &mut x {
        let ptr: *mut T = item_ref;
        unsafe {
            let y1: T = std::ptr::read(ptr);
            let fix_missing_elem = RunOnDrop {
                f: || {
                    let y: TStruct = std::ptr::read(x_ptr);
                    let mut i = 0;
                    for item_ref in &y {
                        let current_item_ptr: *const T = item_ref;
                        if current_item_ptr == ptr {
                            break;
                        }
                        i += 1;
                    }

                    let mut j = 0;
                    for elem_ref in y {
                        if j == i {
                            std::mem::forget(elem_ref);
                        }
                        j += 1;
                    }
                    std::ptr::write(x_ptr, <TStruct as Default>::default());
                },
            };
            let result = f(y1);
            std::mem::forget(fix_missing_elem);
            std::ptr::write(ptr, result);
        }
    }
    x
}

struct RunOnDrop<F: FnMut()> {
    f: F,
}

impl<'a, F: FnMut()> Drop for RunOnDrop<F> {
    fn drop(&mut self) {
        (self.f)()
    }
}
error[E0637]: `&` without an explicit lifetime name cannot be used here
 --> src/lib.rs:6:5
  |
6 |     &mut TStruct: IntoIterator<Item = &mut T>,
  |     ^ explicit lifetime name needed here

error[E0637]: `&` without an explicit lifetime name cannot be used here
 --> src/lib.rs:6:39
  |
6 |     &mut TStruct: IntoIterator<Item = &mut T>,
  |                                       ^ explicit lifetime name needed here

error[E0637]: `&` without an explicit lifetime name cannot be used here
 --> src/lib.rs:7:5
  |
7 |     &TStruct: IntoIterator<Item = &T>,
  |     ^ explicit lifetime name needed here

error[E0637]: `&` without an explicit lifetime name cannot be used here
 --> src/lib.rs:7:35
  |
7 |     &TStruct: IntoIterator<Item = &T>,
  |                                   ^ explicit lifetime name needed here

error[E0310]: the parameter type `T` may not live long enough
  --> src/lib.rs:1:1
   |
1  |   fn inplace_map<T, TStruct: IntoIterator<Item = T> + Default>(
   |   ^              - help: consider adding an explicit lifetime bound `T: 'static`...
   |  _|
   | |
2  | |     f: impl Fn(T) -> T,
3  | |     mut x: TStruct,
4  | | ) -> TStruct
...  |
41 | |     x
42 | | }
   | |_^
   |
note: ...so that the reference type `&'static mut T` does not outlive the data it points at
  --> src/lib.rs:1:1
   |
1  | / fn inplace_map<T, TStruct: IntoIterator<Item = T> + Default>(
2  | |     f: impl Fn(T) -> T,
3  | |     mut x: TStruct,
4  | | ) -> TStruct
...  |
41 | |     x
42 | | }
   | |_^

error[E0310]: the parameter type `TStruct` may not live long enough
  --> src/lib.rs:1:1
   |
1  |   fn inplace_map<T, TStruct: IntoIterator<Item = T> + Default>(
   |   ^                 -------- help: consider adding an explicit lifetime bound `TStruct: 'static`...
   |  _|
   | |
2  | |     f: impl Fn(T) -> T,
3  | |     mut x: TStruct,
4  | | ) -> TStruct
...  |
41 | |     x
42 | | }
   | |_^
   |
note: ...so that the reference type `&'static mut TStruct` does not outlive the data it points at
  --> src/lib.rs:1:1
   |
1  | / fn inplace_map<T, TStruct: IntoIterator<Item = T> + Default>(
2  | |     f: impl Fn(T) -> T,
3  | |     mut x: TStruct,
4  | | ) -> TStruct
...  |
41 | |     x
42 | | }
   | |_^
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Clinton
  • 22,361
  • 15
  • 67
  • 163
  • *the ideas of `replace_with`* — what is this `replace_with` that you are referring to? – Shepmaster Jun 18 '19 at 17:27
  • `where for<'a> &'a mut TStruct: IntoIterator, for<'b> &'b TStruct: IntoIterator,` [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ae7cc40e0bde3826267a6024e64e45e8) – Shepmaster Jun 18 '19 at 17:29

0 Answers0