I was following along with this tutorial on creating a concurrent counter struct for a usize value: ConcurrentCounter. As I understand it, this wrapper struct allows us to mutate our usize value, with more concise syntax, for example:my_counter.increment(1) vs. my_counter.lock().unwrap().increment(1).
Now in this tutorial our value is of type usize, but what if we wanted to use a f32, i32, or u32 value instead?
I thought that I could do this with generic type arguments:
use std::{sync::{Arc, Mutex}};
#[derive(Clone)]
pub struct ConcurrentCounter<T>(Arc<Mutex<T>>);
impl<T> ConcurrentCounter<T>{
pub fn new(val:T) -> Self {
ConcurrentCounter(Arc::new(Mutex::new(val)))
}
pub fn increment(&self, by: T) {
let mut counter = self.0.lock().unwrap();
*counter = *counter + by;
}
pub fn decrement(&self, by: T) {
let mut counter = self.0.lock().unwrap();
*counter = *counter - by;
}
pub fn multiply(&self, by: T) {
let mut counter = self.0.lock().unwrap();
*counter = *counter * by;
}
pub fn get(&self) -> T {
let counter = self.0.lock().unwrap();
*counter
}
}
But this yields error:
cannot add `T` to `T`
cannot subtract `T` from `T`
cannot multiply `T` by `T`
I know I need to provide some trait restrictions for my type arguments, but I'm unsure what exactly those arguments are. I know that you can't do everything you can do with an i32 with a f32, but I just need to use the counter for some simple operations and it seems silly use a different wrapper struct for primitive number types. So how would I get my ConcurrentCounter struct working, or is there maybe a library that provides such a counter?
1 Answer
I haven't come across such a ConcurrentCounter library, but crates.io is huge, maybe you find something. However, if you are mostly concerned with primitives such as i32, there is a better alternative call: Atomics, definitely worth checking out.
Nevertheless, your approach of generalizing the ConcurrentCounter is going in a good direction. In the context of operation overloading, std::ops is worth a look. Specifically, you need Add, Sub, and Mul, respectively. Also, you need a Copy bound (alternatively, a Clone would also do). So you were pretty close:
use std::ops::{Add, Mul, Sub};
use std::sync::{Arc, Mutex};
pub struct ConcurrentCounter<T>(Arc<Mutex<T>>);
impl<T: Copy> ConcurrentCounter<T>{
pub fn new(val:T) -> Self {
ConcurrentCounter(Arc::new(Mutex::new(val)))
}
pub fn increment(&self, by: T) where T: Add<Output=T> {
let mut counter = self.0.lock().unwrap();
*counter = *counter + by;
}
pub fn decrement(&self, by: T) where T: Sub<Output=T> {
let mut counter = self.0.lock().unwrap();
*counter = *counter - by;
}
pub fn multiply(&self, by: T) where T: Mul<Output=T> {
let mut counter = self.0.lock().unwrap();
*counter = *counter * by;
}
pub fn get(&self) -> T {
let counter = self.0.lock().unwrap();
*counter
}
}