Private methods in impl block

This works but feels like I might be breaking some rules.
These are private methods within impl. The cc_update is called by public methods in the impl and it in turn calls the 3 methods above it. The whole thing is wrapped in a Mutex. If its not enough code to demonstrate I can post more or point to the file on github.
I understand the use of self, &self, &mut self. In function cc_put_byte() I am modifying the data in the protected struct but the self parameter is not mutable but is using the mutable MutexGuard to modify the data. I can call this function using CCDataMutex:: and remove the self parameter but it feels more correct the way I have it.
I could just use one method for this but wanted to see how to manage local calls.

// Get the given byte at the given index in cc_array
	fn cc_get_byte(&self, m: &mut MutexGuard<CCData>, array_idx: usize, byte_idx: usize) -> u8 {
		return m.cc_array[array_idx] [byte_idx];
	}

	// Overwrite the given byte at the given index in cc_array 
	fn cc_put_byte(&self, m: &mut MutexGuard<CCData>, array_idx: usize, byte_idx: usize, b: u8) {
		m.cc_array[array_idx] [byte_idx] = b;
	}

	// Given a target bit setting and the current bit field and mask return the modified field
	fn cc_set_bits(&self, bit_setting: u8, bit_field: u8, bit_mask: u8) -> u8 {
		return (bit_field & bit_mask) | bit_setting;
	}

	// Update the given field in cc_array
	fn cc_update(&mut self, array_idx: usize, byte_idx: usize, bit_setting: u8, bit_field: u8, bit_mask: u8) {
		let mut m = self.ccdata_mutex.lock().unwrap();
		let b: u8 = self.cc_get_byte(&mut m, array_idx, byte_idx);
		let new_b: u8 = self.cc_set_bits(bit_setting, bit_field, bit_mask);
		self.cc_put_byte(&mut m, array_idx, byte_idx, new_b);
	}

Is there a question?

Yes, the question is should I use self. or CCDataMutex:: and is what I have the correct way to do things as I am modifying data without a mutable self parameter?

Generally don't pass self if it's not used.

At a higher level, perhaps cc_put_byte should be a method on CCData rather than on this other type.

Not sure if I understand your use case and/or question correctly, but it's possible to have associated functions instead of methods, i.e. they do not use self, &self, or &mut self at all. You can invoke them with Self::associated_function_name.

use std::sync::{Mutex, MutexGuard};

#[derive(Default, Debug)]
struct Fibonacci {
    a: Mutex<i32>,
    b: Mutex<i32>,
}

impl Fibonacci {
    pub fn foo(&self) {
        let mut a_guard = self.a.lock().unwrap();
        let mut b_guard = self.b.lock().unwrap();
        Self::bar(&mut a_guard, &mut b_guard);
        // do something else with the guards here
    }
    fn bar(a: &mut MutexGuard<i32>, b: &mut MutexGuard<i32>) {
        (**a, **b) = (**b, **a + **b)
    }
}

fn main() {
    let fib = Fibonacci {
        a: Mutex::new(1),
        b: Mutex::new(1),
    };
    for _ in 0..6 {
        println!("{fib:?}");
        fib.foo()
    }
}

(Playground)

Does that help?

Yes it does, thanks. I wasn't happy with using self. or CCDataMutex:: as both felt wrong. It does make sense not to have a self parameter if self is not used and to call those functions with Self:: (which as I understand it is the type so equivalent to CCDataMutex:: I think) . For me its worth asking these questions as I go along so I don't repeat a poor pattern.

Generally, yes. But maybe it's worth noting that sometimes you should do it. In particular if you want to use trait objects (with dyn, see Playground, where &self isn't accessed but still necessary).

Yes, when you are inside an associated function or method, you can refer to the type with Self.

By the way, using associated functions is a very common idiom in Rust. It's also used often for the new function (which doesn't take a self as argument). See the corresponding section in the Rust reference.

1 Like