Creating new Rc instances without taking ownership

Viewed 88

Say I have the following code:

use std::rc::Rc;

struct Struct {}

struct Pair {
    first: Rc<Struct>,
    second: Rc<Struct>
}

fn pair_lists(vec:Vec<Struct>) -> Vec<Pair> {
    let mut pair = Vec::new();
    for i in 0..vec.len() {
        for j in i+1..vec.len() {
            let new_pair = Pair {
                first: Rc::new(vec[i]),
                second: Rc::new(vec[j])
            };
            pair.push(new_pair);
        }
    }
    pair
}

fn main() {
}

This will not compile because in the function pair_lists, Rc::new will take ownership of the vec:

error[E0507]: cannot move out of index of `Vec<Struct>`
  --> src/main.rs:15:32
   |
15 |                 first: Rc::new(vec[i]),
   |                                ^^^^^^ move occurs because value has type `Struct`, which does not implement the `Copy` trait

error[E0507]: cannot move out of index of `Vec<Struct>`
  --> src/main.rs:16:33
   |
16 |                 second: Rc::new(vec[j])
   |                                 ^^^^^^ move occurs because value has type `Struct`, which does not implement the `Copy` trait

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0507`.

However, I don't want to create an overhead by copying all the instances of vec; ideally, there should only be one copy of each element in memory.

What is the rust idiomatic way to achieve this?

1 Answers

Convert vec into a Vec<Rc<Struct>> first, and then clone the Rcs from that:

fn pair_lists(vec:Vec<Struct>) -> Vec<Pair> {
    let vec: Vec<Rc<Struct>> = vec
        .into_iter()
        .map(Rc::new)
        .collect();

    let mut pair = Vec::new();
    for i in 0..vec.len() {
        for j in i+1..vec.len() {
            let new_pair = Pair {
                first: Rc::clone(&vec[i]),
                second: Rc::clone(&vec[j]),
            };
            pair.push(new_pair);
        }
    }
    pair
}

Creating the pairs from the vec can also be accomplished by using .tuple_windows() from the itertools crate:

use itertools::Itertools;

fn pair_lists(vec:Vec<Struct>) -> Vec<Pair> {
    vec.into_iter()
        .map(Rc::new)
        .tuple_windows()
        .map(|(first, second)| Pair { first, second, })
        .collect()
}
Related