Reputation: 360
I'm trying to learn Rust and want to write a simple generic swap function
fn swap<T>(i: &mut T, j: &mut T) {
let tmp: T = *i;
*i = *j;
*j = tmp;
}
fn main() {
let i: &int = &mut 5;
let j: &int = &mut 6;
println!("{} {}", i, j);
swap(i, j);
println!("{} {}", i, j);
}
But the compiler throws around with error messages:
helloworld.rs:2:18: 2:20 error: cannot move out of dereference of `&mut`-pointer
helloworld.rs:2 let tmp: T = *i;
^~
helloworld.rs:2:9: 2:12 note: attempting to move value to here
helloworld.rs:2 let tmp: T = *i;
^~~
helloworld.rs:2:9: 2:12 help: to prevent the move, use `ref tmp` or `ref mut tmp` to capture value by reference
helloworld.rs:2 let tmp: T = *i;
^~~
helloworld.rs:3:10: 3:12 error: cannot move out of dereference of `&mut`-pointer
helloworld.rs:3 *i = *j;
^~
helloworld.rs:13:10: 13:11 error: cannot borrow immutable dereference of `&`-pointer `*i` as mutable
helloworld.rs:13 swap(i, j);
^
helloworld.rs:13:13: 13:14 error: cannot borrow immutable dereference of `&`-pointer `*j` as mutable
helloworld.rs:13 swap(i, j);
^
error: aborting due to 4 previous errors
I'm really new to Rust and I really can't deal in any way with this errors. Hopefully someone can explain what goes wrong and why.
Upvotes: 4
Views: 2522
Reputation: 13117
The problem with the pointer dereferencing is it's violating Rust's move semantics. Your function is borrowing references to i
and j
, and even though you are allowed to modify the borrowed value -- for example:
fn assign<T>(i: &mut T, j: T) {
*i = j;
}
is totally fine -- you are not allowed to "move" the borrowed value somewhere else, even to a temporary variable. This is because the borrow only lasts as long as the function's invocation, and by moving the value you are transferring ownership to the function, which is not allowed.
One way to get around this (without using unsafe code) is by copying the value rather than moving it. You can restrict T
to implement the Clone
trait, which allows you to make copies of the values, leaving the original borrowed value untouched:
fn swap<T: Clone>(i: &mut T, j: &mut T) {
let tmp = i.clone();
*i = j.clone();
*j = tmp;
}
fn main() {
let i: &mut int = &mut 5;
let j: &mut int = &mut 6;
println!("{} {}", i, j);
swap(i, j);
println!("{} {}", i, j);
}
Also notice I had to make i
and j
&mut int
, since in your code you were immutably borrowing references to the values.
Upvotes: 9
Reputation: 299790
There are two issues here, the first is that you are losing mutability when doing:
let i: &int = &mut 5;
It should be expressed either as:
let i: &mut int = &mut 5;
// or
let i = &mut 5i;
The latter being of course recommended for being that more terse.
The second issue, however, is simply that swap
is inherently unsafe
because the compiler is a bit too primitive today: it fears that by moving out of i
in swap
the caller would end-up with a reference to a moved-out item (dangling reference) and does not realize that due to the way the function is built you are guaranteeing that something else will fill that hole before the caller gets back in control.
This function is thus provided std::mem::swap
so you do not have to do it yourself. You can peruse its implementation for details.
Upvotes: 4