1

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 1

3

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
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

you might buy yourself a little more flexibility by being generic in the argument type , i.e. pub fn increment<S>(&self, by: S) where S: Add<Output=T>.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.