Hi all,
I am thinking about modifying a shared variable by two different closures. The pseudocode is like this:
def counter(x: Int) = {
val c = new Ref(x) // new a reference referring to a integer 5
(() => c += 1, () => c -= 1)
}
val (increase, decrease) = counter(5)
increase()
decrease() // result: 5
According to kornel's suggestions in Heap-allocated value with associated lifetime, I have implemented the code in two versions:
Rc:
use std::boxed::Box;
use std::cell::Cell;
use std::ops::Fn;
use std::rc::Rc;
fn main() {
fn counter(x: i16) -> (Box<dyn Fn() -> i16>, Box<dyn Fn() -> i16>) {
let v: Rc<Cell<i16>> = Rc::new(Cell::new(x));
let b1 = v.clone();
let b2 = v.clone();
(
Box::new({
move || {
b1.set(b1.get() + 1);
return b1.get();
}
}),
Box::new({
move || {
b2.set(b2.get() - 1);
return b2.get();
}
})
)
}
let (inc, dec) = counter(5);
let res = inc();
println!("{}", res);
let res = inc();
println!("{}", res);
let res = dec();
println!("{}", res);
}
Arc:
use core::sync::atomic::AtomicI16;
use std::boxed::Box;
use std::cell::Cell;
use std::ops::Fn;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::Ordering;
fn main() {
fn counter(x: i16) -> (Box<dyn Fn() -> i16>, Box<dyn Fn() -> i16>) {
let v: Arc<AtomicI16> = Arc::new(AtomicI16::new(x));
let b1 = v.clone();
let b2 = v.clone();
(
Box::new({
move || {
b1.store(b1.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
return b1.load(Ordering::SeqCst);
}
}),
Box::new({
move || {
b2.store(b2.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
return b2.load(Ordering::SeqCst);
}
})
)
}
// Ordering::Relaxed sync in one thread
// Ordering::SeqCst sync among all the threads
let (inc, dec) = counter(5);
let res = inc();
println!("{}", res);
let res = inc();
println!("{}", res);
// drop(state); // uncomment for borrow error
let res = dec();
println!("{}", res);
}
I am wondering which one is a better implementation for my pseudocode? If neither is, could someone tell me the best implementation? Thanks!