Reputation: 103
I need to write a Rust function that can modify state defined in a higher function and propagate it from one function call to another in an iterator (see the pseudo-code below).
In bad C, I would do it using a shared pointer. Of course, I understand why I should not do that, and why I can not do it in Rust.
The workaround I found is to add an extra function parameter and an additional return argument:
fn f(inputs ..., s) {
let mut s = s;
// computations that rely on the value of s
// ...
outputs ..., s
}
fn main() {
let mut s;
for ... {
let (outputs ..., x) = f(inputs ..., s);
s = x;
}
}
This seems a bit heavy in terms of programming style and I hope that there is a lighter construction (maybe more monadic), I imagine using closures. How I should write it is not obvious to me.
Upvotes: 0
Views: 308
Reputation: 19662
The answer lies in references.
A shared pointer in C comes with caveats as to where and when it is okay to do so, and when it isn't. Rust has a borrow checker to make sure that you're not doing anything stupid/unsafe with pointers to prevent a large part of the issues people get bitten by in C.
For instance, consider a slight variation your code (made purely so it compiles). One can rewrite it as follows:
fn do_with(s: &mut u8, item: u8) {
*s = *s + item;
}
fn main() {
let mut s: u8 = 0;
(1..10).for_each(|item| do_with(&mut s, item));
println!("{}", s)
}
You'll recognize this syntax from C, but it is a lot safer than C as the borrow checker ensures that your code only ever has one mutable reference at any given time. As a result, the code is considered sane at every step and compiles.
You can also do it the way you did, by effectively trampolining the value from call to call. However, that does lead to less readable code and one more level of indirection. Example below:
fn do_with(s: u8, item: u8) -> u8 {
s + item
}
fn main() {
let mut s: u8 = 0;
(1..10).for_each(|item| s = do_with(s, item));
println!("{}", s)
}
In C, the danger in pointers mostly lies in upkeep and maintenance regarding them. As Rust checks and enforces this for you (and this - positively - prevents you from shooting yourself in the foot whenever you're using futures
), making this largely a non-issue.
Upvotes: 2