Gilles
Gilles

Reputation: 5407

Cannot move out of dereference of `&`-pointer

I have the following code

pub struct PropertyDeclarationBlock {
    pub declarations: Arc<Vec<(PropertyDeclaration, PropertyDeclarationImportance)>>
}

impl PropertyDeclarationBlock {
    pub fn select_declarations(&self) -> Arc<Vec<PropertyDeclaration>> {
        Arc::new(self.declarations.clone().map_in_place(|p| {
                                                        let (declaration, _) = p;
                                                        declaration
                                                        }))
    }

}

I want to be able to call .select_declarations() on a PropertyDeclarationBlock and have it return a clone of the declarations but instead of it being a Arc Vec (PropertyDeclaration, PropertyDeclarationImportance) be only a Arc Vec PropertyDeclaration, in other words returning a a vector of PropertyDeclaration instead of the previous tuple.

The previous won't compile as I am getting the following error:

error: cannot move out of dereference of `&`-pointer
Arc::new(self.declarations.clone().map_in_place(|p| {
         ^~~~~~~~~~~~~~~~~~~~~~~~~

From what I understand, since the function take self as a parameter it will take ownership of it. Since I'd rather have the function burrow self I use the &.

EDIT

Here is the error message after implementing the new function:

error: cannot move out of dereference of `&`-pointer
Arc::new(self.declarations.iter().map(|&(declaration, _)| declaration).collect())
                                       ^~~~~~~~~~~~~~~~~
note: attempting to move value to here (to prevent the move, use `ref declaration` or `ref mut declaration` to capture value by reference)
Arc::new(self.declarations.iter().map(|&(declaration, _)| declaration).collect())
                                         ^~~~~~~~~~~

I tried applying the ref keyword before declaration as suggested by the error message but it didn't help.

Upvotes: 0

Views: 2320

Answers (1)

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65712

self.declarations.clone() clones the Arc, while I believe you intended to clone the Vec. To resolve the call to map_in_place, the compiler automatically dereferences the Arc to be able to call the method, which is defined on Vec. The compiler knows how to dereference the Arc because Arc implements Deref. deref returns a borrowed pointer, and this is where the error comes from. To clone the Vec, we must explicitly dereference the Arc: (*self.declarations).clone().

However, map_in_place is not suitable in this case, as (PropertyDeclaration, PropertyDeclarationImportance) doesn't have the same size as PropertyDeclaration (unless PropertyDeclarationImportance has a size of zero, which is probably not the case), which is required per the docs. It fails with a message similar to this:

task '<main>' failed at 'assertion failed: mem::size_of::<T>() == mem::size_of::<U>()', /build/rust-git/src/rust/src/libcollections/vec.rs:1805

Here's a proper implementation:

impl PropertyDeclarationBlock {
    pub fn select_declarations(&self) -> Arc<Vec<PropertyDeclaration>> {
        Arc::new(self.declarations.iter().map(|&(declaration, _)| declaration).collect())
    }
}

Here, we use iter on the Vec to create an iterator over the vector's items. This returns an Items, which implements Iterator on immutable references.

Then, we use map to lazily map (PropertyDeclaration, PropertyDeclarationImportance) to PropertyDeclaration. Note how we destructure the tuple and the reference in the closure's parameter list (this works in fns too) instead of using a let statement.

Finally, we use collect to create a new collection container for this sequence. collect's result is generic; it can be any type that implements FromIterator. Vec implements FromIterator, and the compiler infers Vec from the method's signature.

Upvotes: 1

Related Questions