Reputation: 4138
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
Reputation: 90902
There are a few mistakes in your code.
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
.
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