Lily Mara
Lily Mara

Reputation: 4138

Moving and returning a mutable pointer

I am working through this Rust tutorial, and I'm trying to solve this problem:

Implement a function, incrementMut that takes as input a vector of integers and modifies the values of the original list by incrementing each value by one.

This seems like a fairly simple problem, yes?

I have been trying to get a solution to compile for a while now, and I'm beginning to lose hope. This is what I have so far:

fn main() {
    let mut p = vec![1i, 2i, 3i];
    increment_mut(p);

    for &x in p.iter() {
        print!("{} ", x);
    }
    println!("");
}

fn increment_mut(mut x: Vec<int>) {
    for &mut i in x.iter() {
        i += 1;
    }
}

This is what the compiler says when I try to compile:

Compiling tut2 v0.0.1 (file:///home/nate/git/rust/tut2)
/home/nate/git/rust/tut2/src/main.rs:5:12: 5:13 error: use of moved value: `p`
/home/nate/git/rust/tut2/src/main.rs:5  for &x in p.iter() {
                                                  ^
/home/nate/git/rust/tut2/src/main.rs:3:16: 3:17 note: `p` moved here because it has type `collections::vec::Vec<int>`, which is non-copyable
/home/nate/git/rust/tut2/src/main.rs:3  increment_mut(p);
                                                      ^
error: aborting due to previous error
Could not compile `tut2`.

To learn more, run the command again with --verbose.

I also tried a version with references:

fn main() {
    let mut p = vec![1i, 2i, 3i];
    increment_mut(&p);

    for &x in p.iter() {
        print!("{} ", x);
    }
    println!("");
}

fn increment_mut(x: &mut Vec<int>) {
    for &mut i in x.iter() {
        i += 1i;
    }
}

And the error:

Compiling tut2 v0.0.1 (file:///home/nate/git/rust/tut2)
/home/nate/git/rust/tut2/src/main.rs:3:16: 3:18 error: cannot borrow immutable dereference of `&`-pointer as mutable
/home/nate/git/rust/tut2/src/main.rs:3  increment_mut(&p);
                                                      ^~
error: aborting due to previous error
Could not compile `tut2`.

To learn more, run the command again with --verbose.

I feel like I'm missing some core idea about memory ownership in Rust, and it's making solving trivial problems like this very difficult, could someone shed some light on this?

Upvotes: 1

Views: 366

Answers (1)

Chris Morgan
Chris Morgan

Reputation: 90902

There are a few mistakes in your code.

  1. increment_mut(&p), given a p that is Vec<int>, would require the function increment_mut(&Vec<int>); &-references and &mut-references are completely distinct things syntactically, and if you want a &mut-reference you must write &mut p, not &p.

  2. You need to understand patterns and how they operate; for &mut i in x.iter() will not do what you intend it to: what it will do is take the &int that each iteration of x.iter() produces, dereference it (the &), copying the value (because int satisfies Copy, if you tried it with a non-Copy type like String it would not compile), and place it in the mutable variable i (mut i). That is, it is equivalent to for i in x.iter() { let mut i = *i; … }. The effect of this is that i += 1 is actually just incrementing a local variable and has no effect on the vector. You can fix this by using iter_mut, which produces &mut int rather than &int, and changing the &mut i pattern to just i and the i += 1 to *i += 1, meaning “change the int inside the &mut int.

You can also switch from using &mut Vec<int> to using &mut [int] by calling .as_mut_slice() on your vector. This is a better practice; you should practically never need a reference to a vector as that is taking two levels of indirection where only one is needed. Ditto for &String—it’s exceedingly rare, you should in such cases work with &str.

So then:

fn main() {
    let mut p = vec![1i, 2i, 3i];
    increment_mut(p.as_mut_slice());

    for &x in p.iter() {
        print!("{} ", x);
    }
    println!("");
}

fn increment_mut(x: &mut [int]) {
    for i in x.iter_mut() {
        *i += 1;
    }
}

Upvotes: 6

Related Questions