-1

I have a minimal example of code that implements move semantics for some container:

use std::mem;

impl<'a, T: 'a + ?Sized> Drop for Cointainer<'a, T> {
    fn drop(&mut self) {}
}

struct Cointainer<'a, T: 'a + ?Sized> {
    item: &'a /* mut */ T,
}

impl<'a, T> Cointainer<'a, T> {
    fn mv(self) -> Cointainer<'a, T> {
        let new = Cointainer { item: /* &mut */ self.item };
        mem::forget(self);
        new
    }
}

fn main() {}

That compiles and works without any problems.

I realized I will need to mutate the value referenced by Cointainer::item, so I've made the reference mut. When I do so, I get:

error[E0505]: cannot move out of `self` because it is borrowed
  --> src/main.rs:14:21
   |
13 |         let new = Cointainer { item: /* &mut */ self.item };
   |                                                 --------- borrow of `*self.item` occurs here
14 |         mem::forget(self);
   |                     ^^^^ move out of `self` occurs here

I need to create a new container and transfer ownership of item there, and drop the old one.

This example is artificial. The actual "move" operation does some other stuff, and doesn't necessarily return the same container type.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
LOST
  • 2,956
  • 3
  • 25
  • 40
  • 2
    I think you might have simplified the problem too much. For example, if your real `Cointainer` is `Drop` (as the `forget` call suggests) the situation is more complicated. – Sebastian Redl Jan 28 '18 at 10:56
  • @SebastianRedl yeah, definitely it does. That's the whole point. – LOST Jan 28 '18 at 12:11
  • @Shepmaster , if you need a more concrete example, then you can take a look at https://github.com/rphmeier/allocators/blob/master/src/boxed.rs AllocBox::downcast. There might be a better way to do that, but I'd like to understand why the original issue occurs, and how to circumvent it. It is clear to me, that what I want to do is safe. I don't understand, however, how to express it in Rust. – LOST Jan 28 '18 at 16:50
  • *Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and **the shortest code necessary to reproduce it in the question itself**.*. It's not reasonable to require that people looking to answer your question go to an off-site resource. – Shepmaster Jan 28 '18 at 16:53
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/164042/discussion-between-lost-and-shepmaster). – LOST Jan 28 '18 at 16:56

1 Answers1

1

The rules of references state:

  1. At any given time, you can have either but not both of:

    • One mutable reference.
    • Any number of immutable references.

Your code with immutable references works because those can be freely copied. Your code with mutable references fails because, as far as the compiler can tell, you would need to have have two concurrent mutable references: the saved reference in new and then mem::forget might also need it.

As humans, we recognize that mem::forget will not access the guts of our structure. This is what unsafe code is for: when the compiler cannot guarantee the code we have is truly safe.

A small unsafe block and some casting to a raw pointer and back solves the problem. As with any unsafe code, it should have a big comment block explaining why the compiler doesn't understand it and why it's truly safe.

impl<'a, T: 'a + ?Sized> Drop for Cointainer<'a, T> {
    fn drop(&mut self) {
        println!("dropping 1: {:p}", self.item)
    }
}

struct Cointainer<'a, T: 'a + ?Sized> {
    item: &'a mut T,
}

impl<'a, T> Cointainer<'a, T> {
    fn into_inner(self) -> &'a mut T {
        // I copied this code from Stack Overflow but didn't read
        // the warning about explaining why it's safe. Sure hope
        // this doesn't cause any bugs!
        unsafe {
            let x = self.item as *mut _;
            std::mem::forget(self);
            &mut *x
        }
    }

    fn mv(self) -> Cointainer2<'a, T> {
        let item = self.into_inner();
        Cointainer2 { item }
    }
}

struct Cointainer2<'a, T: 'a + ?Sized> {
    item: &'a mut T,
}

impl<'a, T: 'a + ?Sized> Drop for Cointainer2<'a, T> {
    fn drop(&mut self) {
        println!("dropping 2: {:p}", self.item)
    }
}

fn main() {
    let mut a = String::new();
    let c1 = Cointainer { item: &mut a };
    let c2 = c1.mv();
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366