This generates an error about moving out of a borrow, but it is actually safe because we move the modified value back into x. It is possible to rewrite this:
Which seems functionally equivalent to me, except it requires an additional function which clutters the API.
Could the borrow checker be changed to allow this pattern?
Edit: thinking more about this, I can see why it is not allowed, because 'succ' could panic after moving 'x', but it also seems that it should somehow be possible to to apply the 'succ' function as a mutation of x.
You need to consider exception safety. What happens if succ panics?
use std::mem;
struct Foo {
s: String
}
impl Drop for Foo {
fn drop(&mut self) {
println!("I can see `{}`", self.s);
}
}
fn boo(s: String) -> String {
drop(s);
panic!();
}
/* What would the destructor print if this compiled?
fn foo(s: &mut String) {
*s = boo(*s);
}
*/
fn bar(s: &mut String) {
// this is known as "pre-poop your pants"
let t = mem::replace(s, "Dummy".to_string());
mem::replace(s, boo(t));
}
fn main() {
let mut f = Foo { s: "Hello!".to_string() };
bar(&mut f.s)
}
But it is less efficient because of the copy, which isn't necessary, it the compiler could recognise the pattern and replace with the appropriate alternative. (see + vs += above).
If the type has a cheap default, you could swap it out and then write back the result. That's basically what @gkoz did with the "Dummy" value.
let y = ::std::mem::replace(x, T::default());
*x = y.succ();
There's still some shallow copying as you move values around, but I think that's the best you can hope for without explicitly modifying in-place (like your inc).
I think there's a crate that provides an unsafe function which can apply an fn(T) -> T to an &mut T (it moves the T out of the borrow, leaving the value under the reference temporarily unitialized). Its unsafe because its your responsibility to guarantee the function you pass won't panic.
I can't remember what its called though. Does anyone remember what I'm talking about?
The answer is probably to give up writing "x = x + 1" and get used to mutating the data through a reference as in "x += 1", and avoid unsafe solutions, although it seems that a more sophisticated compiler might be able to rewrite one into the other.
As it makes sense to have both versions, because one is necessary for borrowed collections, and the other where you have a temporary, so you can use like this
x.half_nonnegative_mut()
let y = x.clone().half_nonnegative()
What would the typical way to differentiate between the functions in Rust:
Doesn't a _mut suffix usually distinguish cases where the output may or may not be mutable? I'm thinking like slice::chunks vs. slice::chunks_mut, where they otherwise behave the same.
I think the into_ prefix is more common to take ownership and return something else.
If I just saw half_nonnegative, my intuition would be that this is something taking &self and returning a new value. I'm not sure what to call the in-place modification though. Perhaps it needs an active verb -- halve_nonnegative?