How to convert Vec<Arc<Item>> to Vec<Item>?

I'm pretty new to Rust, how to make below code works:

struct Items {
    maps: HashMap<KeyType, Vec<Arc<ValueType>>>,
}

impl Items {
    pub fn get_value(&self, key: &KeyType) -> Option<&Vec<ValueType>> {
        // How?
    }
}

fn main() {
    let items = Items::new();

    // insert some key and value

    if let Some(results) = items.get_value(key) {
        // results is &Vec<ValueType>
    }
}

Or I'm on the wrong way, like Arc shouldn't in a Vec. Any suggestions are welcome :slight_smile:

As written, you can't make the get_value() function work. An Arc is a pointer, it represents indirection, so a Vec<Arc<ValueType>> stores pointers in the vector, whereas a Vec<ValueType> stores the values directly, in a contiguous buffer, without intermediary pointers.

Therefore, any conversion between Vec<Arc<ValueType>> and Vec<ValueType> will involve either the moving or copying of some objects – incurring a fresh allocation of a vector, which therefore isn't possible to return "by reference". So first of all, you'll have to change your function signature to return -> Option<Vec<ValueType>>.

If you know every value will be owned by exactly one Arc, you could steal each pointed object and return a Vec<ValueType>, using Arc::try_unwrap().

That would, however, require you to pass the (last and only) Arc instance by-value, of course, which you can't do if you are .get()ting it out of a HashMap. So you would either need to remove them from the HashMap or simply clone the inner value, which in turn requires impl Clone for ValueType, like this:

impl Items {
    pub fn get_value(&self, key: &KeyType) -> Option<Vec<ValueType>> {
        self.maps.get(key)
            .map(|arcs| {
                arcs.iter()
                    .map(|arc| ValueType::clone(&*arc))
                    .collect()
            })
    }
}

If you can't or don't want to clone the objects, another option would be to just expose the internal representation, and return an Option<&[Arc<ValueType>]>, and let the users of your function deal with the fact that they've got a bunch of pointers back.

(Also note that I changed the signature from &Vec<T> to &[T]. There's no value in returning or accepting an immutable Vec by reference, since anything an immutable reference-to-Vec can do, an immutable reference-to-slice can also do.)

5 Likes

Wah~ Thank you so much. I learned a lot from your post, not just the question, but also other knowledges.

If you want to hide the fact that you’re using Arc internally, you can also declare the return type as Option<&[impl Deref<Target = ValueType>]>. Callers won’t be allowed to use Arc-specific functionality, so you can later change to some other pointer type without breaking them.

4 Likes

Nice~ Another magic