Newbie, impl, and how to consume his vector "cannot move out of borrowed content" [solved]

I am slowly getting Rust to do cool things, but ownership inside a struct is confusing me.

I reproduced a really simple example of how I am blocked:

struct ImplOwnership {
  stuff_i_want_to_come_and_go: Vec<i32>,
}

impl ImplOwnership {
  fn add_number(&mut self, the_number: i32) {
    self.stuff_i_want_to_come_and_go.push(the_number);
  }

  fn print_numbers(&mut self) { // Intend this to consume the list.
    for one_number in self.stuff_i_want_to_come_and_go {
      println!("{}", one_number);
    }
  }
}

fn main() { // Doesn't do anything yet...gotta compile first.
}

But the compiler doesn't like that:

impl-ownership.rs:11:23: 11:27 error: cannot move out of borrowed content [E0507]
impl-ownership.rs:11     for one_number in self.stuff_i_want_to_come_and_go {

Who the heck did I borrow it from?

Thanks,

-kb, the Kent who, bit by bit, is getting Rust to do everything he wants.

Intend this to consume the list.

You can't just consume something and leave nothing behind (well, you can use an Option and leave a None behind but None is still "something"). TL;DR, you probably want to use Vec::drain.

for one_number in self.stuff_i_want_to_come_and_go.drain(0..) {
    // ...
}

However, if you just want to read the least (you don't want to drain it), you can iterate over it by reference:

for one_number in &self.stuff_i_want_to_come_and_go {
    // ...
}

If you want to mutate it as you iterate you have to iterate by mutable reference:

for one_number in &mut self.stuff_i_want_to_come_and_go {
    // ...
}

However, you may also be interested in reading core::iter - Rust.

(edit: fix drain invocation)

2 Likes

Looks like adding a ".drain(0..)" works.

Still not out of the woods for that general section of my code, but this problem looks solved!

Thanks!

-kb, the Kent who has still more work to do.

A new, slightly related problem...

Now I am being broken about access to mutable self itself.

My current example code:

struct ImplOwnership {
  stuff_i_want_to_come_and_go: Vec<i32>,
}

impl ImplOwnership {
  fn add_number(&mut self, the_number: i32) {
    self.stuff_i_want_to_come_and_go.push(the_number);
  }

  fn print_numbers(&mut self) { // Intend this to consume the list.
    for one_number in self.stuff_i_want_to_come_and_go.drain(0..) {
      self.something_else();
      println!("{}", one_number);
    }
  }

  fn something_else(&mut self) {
  }
}

Not good:

impl-ownership2.rs:12:7: 12:11 error: cannot borrow *self as mutable more than once at a time [E0499]

I think this one makes more sense, the whole self is being guarded by Rust's vigilance! But I don't want to "borrow" it, I just want it. I tried grabbing the Vec in a variable, and stuffing a new empty Vec into the Struct, but that didn't work. I think I am thinking too much like a Python programmer and taking the wrong approach. Is there a canonical "No, do it this way when you want to double-dip into your impl..."? How does a Rust programmer approach the kind of encapsulation I am trying here?

Thanks,

-kb

1 Like

You can replace the vector with Vec::new() using the mem::replace, which comes handy in many of similar situations. That function's docs have an example quite similar to your case.

2 Likes

Yes, that works. Looks like I have to think about "self" as a real parameter and not just a ritual I frequently type.

Because I like when I Google around and find complete examples, allow me to paste in my now-working example code:

use std::mem::replace;

struct ImplOwnership {
  stuff_i_want_to_come_and_go: Vec<i32>,
}

impl ImplOwnership {
  fn add_number(&mut self, the_number: i32) {
    self.stuff_i_want_to_come_and_go.push(the_number);
  }

  fn print_numbers(&mut self) { // Intend this to consume the list.
    let mut my_copy = replace(&mut self.stuff_i_want_to_come_and_go, Vec::new());
    for one_number in my_copy {   // because it is mine, all mine, I don't anymore need:  .drain(0..) {
      self.something_else();
      println!("{}", one_number);
    }
  }

  fn something_else(&mut self) {
    // Seems this method can access self only because the code calling it
    // grabbed the vec free and clear.
  }
}

fn main() {
  let mut my_junk = ImplOwnership { stuff_i_want_to_come_and_go: Vec::new() };
  my_junk.add_number(32);
  my_junk.add_number(12);
  my_junk.print_numbers();
  println!("again...");
  my_junk.print_numbers();
}

Thanks all!

-kb, the Kent who likes that, unlike C, once he has finally quieted the Rust compiler complaints, that lack of complaining actually means something.

FYI, that method can be slightly inefficient depending on your usage patterns. Basically, draining the vector doesn't deallocate any allocated space but replacing it with a new vector will. So, if the size of your vector usually has between 0 and some small number (<~100) elements, you should consider either:

  1. Swapping the old vector back in after draining it.
  2. Taking from the back of the vector using pop() (if you don't care about order). That is:
while let Some(item) = self.stuff_i_want_to_come_and_go.pop() {
    // `self` will not be borrowed in here so you're free to mutate it.
    /* ... */
}

Otherwise, you'll constantly be growing and shrinking chunk of allocated memory backing the vector.


However, if performance isn't really that important, don't bother with any of this; you'll just needlessly complicate your code. I just felt you should be warned.