-1

I'd like to move some Boxed values from one Vec to another, but I can't figure out if this is possible or whether I'm thinking about it wrong.

So far, my smallest example of the problem is:

#[derive(Debug)]
pub struct B {
    value:      usize
}

fn main() {
    let v1: Vec<_> = (1..3).map(|i| Box::new(B{ value: i})).collect();
    let v2: Vec<Box<B>> = v1.iter().map(|b| *b).collect();
    println!("{v2:?}");
}

Which gives the error:

error[E0507]: cannot move out of `*b` which is behind a shared reference
 --> src/main.rs:8:45
  |
8 |     let v2: Vec<Box<B>> = v1.iter().map(|b| *b).collect();
  |                                             ^^ move occurs because `*b` has type `Box<B>`, which does not implement the `Copy` trait

I've tried various (but possibly incorrect) methods of trying to move the contents of the Box with std::mem::swap and friends, but haven't managed to get that right.

For clarity, I want to avoid copying the B's - in reality they are large. I haven't experimented with an Rc, and ideally I'd avoid that. v1 is effectively consumed when it is mapped into v2.

As shown above, the example is pretty uninteresting, in the "real" case, v1 is a Vec of 2-tuples, and I'd like to map it to a Vec of 3-tuples with an extra element derived from the first two.

I'm aware I don't need all the type annotation, but that helps check I'm getting what I want. For a while I thought I had succeeded, but was actually getting a Vec<&Box<B>> (or similar, don't take my exact word for it).

Does anyone have any insight as to how to do this, or how I can approach the problem differently? Thanks!

Geoff
  • 253
  • 2
  • 9
  • 2
    The problem in question is not clear to me: you say that "`v1` is effectively consumed when it is mapped to `v2`", but when you write `v1.iter()`, you are *borrowing* v1, so you can't *move* anything, because you don't have access to the values, only references. So your only choice is to clone the box (and its contents). Rust doesn't do "effectively moved", something is either actually moved or it's not. If you want to consume `v1` in the creation of `v2` you can do that, by calling `v1.into_iter()`. This will create an "owning" iterator from the vector itself, and thus will move the contents. – Masklinn Apr 06 '23 at 11:46
  • If `v1` is to be consumed, then just do `let v2 = v1.into_iter().collect();` plus, presumably, whatever transformations you want on the items. – cdhowie Apr 06 '23 at 11:46
  • Sorry @Masklinn, my language wasn't clear, probably because I don't fully understand the concepts involved. I probably should have said something like "I don't need to use v1 once v2 is created, so it can relinquish ownership". `into_iter` is the answer I was looking for. Thank you! – Geoff Apr 06 '23 at 19:29

1 Answers1

3

If you want to consume the orginial vector you want into_iter not iter which only gives you shared references:

let v1: Vec<_> = (1..3).map(|i| Box::new(B{ value: i})).collect();
let v2: Vec<Box<B>> = v1.into_iter().collect();
cafce25
  • 15,907
  • 4
  • 25
  • 31