rust

Swapping values within two pinned boxes


Suppose I have two Pin<Box<T>>s, e.g. I got them from Box::pin(struct_of_type_T), a Box owns the pointer to the underlying value of type T, a Pin ensures that the value (of type T) to which the pointer is pointing to is never moved.

Can I swap the two pointers within the 'Pinned-box'?

 pinned_box_1                   pinned_box_2
|-------------|                |-------------|
| pointer_one |<--swap them--?>| pointer_two |
|-------------|                |-------------|
       |                              |
       V                              V
 struct_of_type_T_one          struct_of_type_T_two
|-------------|                |-------------|
|  value_one  |                | value_two   |
|-------------|                |-------------|
 fixed_addr                     fixed_addr

If yes, how do I swap them? Will std::mem::swap work in this case?


Solution

  • The answer to the body of your question is yes, you can, you don't even need std::mem::swap:

    fn main() {
        let mut pointer_one = Box::pin(99);
        let mut pointer_two = Box::pin(88);
        
        println!("pointer_one: {0:p}, {0}", pointer_one);
        println!("pointer_two: {0:p}, {0}", pointer_two);
        
        std::mem::swap(&mut pointer_one, &mut pointer_two);
        // (pointer_one, pointer_two) = (pointer_two, pointer_one); //also works
    
        println!("\nafter swap\n");
        
        println!("pointer_one: {0:p}, {0}", pointer_one);
        println!("pointer_two: {0:p}, {0}", pointer_two);
    }
    

    output:

    pointer_one: 0x558a90ebb9d0, 99
    pointer_two: 0x558a90ebb9f0, 88
    
    after swap
    
    pointer_one: 0x558a90ebb9f0, 88
    pointer_two: 0x558a90ebb9d0, 99
    

    This is possible because Pin only makes guarantees about the pointee, not the pointer itself, like any other value the pointer remains movable.

    The answer to the title of your question is no, it's not possible to swap two values behind pinned pointers, that's exactly what Pin was made to prevent, the moving of the values.

    There is one exception, if the type behind the pointer implemnts Unpin that signals it's save to move values of that type and Pin<Ptr<T>> will implement DerefMut allowing a swap.