Suppose I have code like this.
fn main() {
let mut x = 123;
let mut y = 456;
let mut z = 789;
x += y; y *= z; z %= x; // random operations
x = 1;
x += y; y *= z; z %= x;
z = z & y;
x += y; y *= z; z %= x;
println!("{} {} {}", x, y, z);
}
Quite contrived, but hopefully the idea is clear --- I have these local variables that I manipulate in a certain complex way more than once.
Obviously I'd like to avoid the duplication and refactor out that manipulation. At the same time, there are so many variables involved that I'd prefer not to pass all of them as mutable reference arguments to an outside utility function.
If this were, say, C++, I might stash the variables in a global scope and define another function, or in more recent versions, use a capturing lambda. I tried to do the corresponding thing in Rust with a closure:
fn main() {
let mut x = 123;
let mut y = 456;
let mut z = 789;
let f = || {
x += y; y *= z; z %= x; // random operations
};
f();
x = 1;
f();
z = z & y;
f();
println!("{} {} {}", x, y, z);
}
But this doesn't compile, because Rust tells me I borrowed x, y, z
to f
. It seems the very existence of f
means I can't use x, y, z
in main
anymore. This Rust error would make sense if I were trying to pass f
out of the function or spawn a process with it or something, but I'm not, and it's evidently safe if I "manually inline" the function as in the first version.
Is there a neat Rust way to do this?
By the way, I found this question and I see that I can avoid an error in the last println!
by declaring f
inside a block that terminates before that statement, but that doesn't help me if I want to mutate the variables in between calls to f
.
One of the simpler ways of doing this is with a macro:
macro_rules! f {
($x:ident, $y:ident, $z:ident) => {{
$x += $y;
$y *= $z;
$z %= $x;
}}
}
This can be invoked with f!(x, y, z)
. It’s not much nicer than writing a function which takes three &mut int
arguments, however.
Now in this specific case, because x
, y
and z
are in scope, provided you write the macro after them you can use x
, y
and z
without needing to pass them through the macro call (recall that Rust’s are hygienic macros). That is, you can write
macro_rules! f {
() => {{
x += y;
y *= z;
z %= x;
}}
}
Here’s the code in full:
#![feature(macro_rules)]
fn main() {
let mut x = 123i;
let mut y = 456i;
let mut z = 789i;
macro_rules! f {
() => {{
x += y;
y *= z;
z %= x;
}}
}
f!()
x = 1;
f!()
z = z & y;
f!()
println!("{} {} {}", x, y, z);
}
In the playpen: http://is.gd/sCaYMS
One solution is to use a Cell. The contents of a Cell are mutable regardless of the Cell's mutability. It will make the code uglier but at least it compiles. Performance shouldn't be affected as far as I know.
use std::cell::Cell;
fn main() {
let x = Cell::new(123i);
let y = Cell::new(456i);
let z = Cell::new(789i);
let f = || {
x.set(x.get() + y.get());
y.set(y.get() * z.get());
z.set(z.get() % x.get());
};
f();
x.set(1);
f();
z.set(z.get() & x.get());
f();
println!("{} {} {}", x, y, z);
}
Probably a better solution is to abstract the data and the functions operating on them as struct.
#[deriving(Show)]
struct Data {
x: int,
y: int,
z: int
}
impl Data {
fn f(&mut self) {
self.x += self.y;
self.y *= self.z;
self.z %= self.x;
}
}
fn main() {
let mut data = Data { x: 123, y: 456, z: 789 };
data.f();
data.x = 1;
data.f();
data.z = data.z & data.x;
data.f();
println!("{}", data);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With