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


#1

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.


#2

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 https://doc.rust-lang.org/nightly/core/iter/index.html#for-loops-and-intoiterator.

(edit: fix drain invocation)


#3

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.


#4

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*selfas 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


#5

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.


#6

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.


#7

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.