Indexing 2d array

I have several issues with this code so ignore the unsafe etc but the first is indexing the 2d array. For some reason it panics on row 5 although there are 7 rows.

[0, 0, 0, 0, 0]
[2, 0, 0, 0, 0]
[4, 0, 0, 0, 0]
[6, 0, 0, 0, 0]
thread 'main' panicked at 'index out of bounds: the len is 4 but the index is 4', src\protocol\cc_out.rs:197:26

cc_out_next_seq() is called seven times just for testing.

pub struct CCData{
	// Default array contains the C0 values that define how C1-C4 are defined
	cc_array : [[u8; 5];7],
	// Single row of the array is returned as next in sequence
	cc_el : [u8; 5],
}

// Implementation methods on CCData
impl CCData {
	// Create a new instance and initialise the default arrays
	pub fn new() -> CCData {
		CCData {  
			cc_array: (
				[
					[ 0x00, 0x00, 0x00, 0x00, 0x00 ],
					[ 0x02, 0x00, 0x00, 0x00, 0x00 ],
					[ 0x04, 0x00, 0x00, 0x00, 0x00 ],
					[ 0x06, 0x00, 0x00, 0x00, 0x00 ],
					[ 0x08, 0x00, 0x00, 0x00, 0x00 ],
					[ 0x12, 0x00, 0x00, 0x00, 0x00 ],
					[ 0x14, 0x00, 0x00, 0x00, 0x00 ],
				]
			),
			cc_el: ([ 0x00, 0x00, 0x00, 0x00, 0x00 ]),
		}
	}

	// Return the next CC data in sequence
	pub fn cc_out_next_seq(&mut self) -> [u8; 5] {
		unsafe {
			self.cc_el = self.cc_array[0..4][CC_IDX];
			CC_IDX = CC_IDX + 1;
			if CC_IDX > RR_CC {
				CC_IDX = 0;
			}
		};
		return self.cc_el;
	}
}

In Rust the indexing order would be arr[row_idx][col_idx]. So what I think you want is self.cc_el = self.cc_array[CC_IDX];. This works because the type of self.cc_array is [[u8; 5]; 7], so self.cc_array[CC_IDX] will be of type [u8; 5].
Sidenote: The assignment of [u8; 5] works because there is a Copy implementation for [T; N] when T is itself Copy.

I recommend you go back to the drawing board and start by removing that unsafe block and figuring out how to do this without unsafe.

Your problem, however, is this line:

In particular, you're taking a slice of 0..4 from self.cc_array. Here's some code to illustrate the problem: Rust Playground

1 Like

Thanks. Yes self.cc_array[CC_IDX] works. I did have that at some point but probably had something else wrong at the time. I should have gone back and tried it again as logically it should have worked. I need to work through how to manage this all now. The main array has multiple bit fields which are updated through a slew of functions that can be called from various places. At regular intervals the next array in the sequence is got and forms part of a transmitted data frame. The data isn't shared so I don't think Arc helps in this instance. It's more of a traditional mutex lock unless Rust has another trick up its sleeve.
I'm returning self.cc_el.clone() now which I think is probably good enough. The unsafe is presumably because CC_IDX needs to be protected against multiple updates.

Well, unsafe isn't protecting anything here. If you are sure that concurrent access is not possible, then you can make a "null-lock". Otherwise a Mutex is the way to go.
On a sidenote, I think the code uses a static mut - hence the unsafe. That is a huge anti-pattern and is almost never the right thing to do. Even if it is super convenient.

1 Like

I used unsafe just so it would compile to test the indexing. There would only be one caller to cc_out_next_seq() so CC_IDX isn't the issue but the array could be updating while I'm pulling out a row so I think it's probably mutex all round in the module. As to static I have a whole load of static definitions in there like.

static CCO_SPEED_B: [u8; 4] = [ 0x00, 0x01, 0x10, 0x11 ];
static CCO_SPEED_M: u8 = 0xfc;

which are read only bit settings and mask as well as the ones you refer to. I nearly put them all in the struct. Would that be the right thing to do?

Moving on I figured that 'static mut' is bad but 'static' is ok. So I move the 'static mut' vars into the struct which gets rid of the unsafe. I've wrapped the struct in a CCDataMutex and moved the cc_out_next_seq() into the impl. It works but not sure I've done it right. I notice that Mutex has no unlock() so does it just rely on the lock() going out of scope? I think it must unlock as I can call cc_out_next_seq() multiple times although from the same thread so not sure if that counts as a test.

pub struct CCData{
	// Current index into array
	cc_idx: usize,
	// Default MOX state
	cc_mox_state: bool,
	// Default array contains the C0 values that define how C1-C4 are defined
	cc_array : [[u8; 5];7],
	// Single row of the array is returned as next in sequence
	cc_el : [u8; 5],
}

// Implementation methods on CCData
impl CCData {
	// Create a new instance and initialise the default arrays
	pub fn new() -> CCData {
		CCData {
			cc_idx: 0,
			cc_mox_state: false,
			cc_array: (
				[
					[ 0x00, 0x00, 0x00, 0x00, 0x00 ],
					[ 0x02, 0x00, 0x00, 0x00, 0x00 ],
					[ 0x04, 0x00, 0x00, 0x00, 0x00 ],
					[ 0x06, 0x00, 0x00, 0x00, 0x00 ],
					[ 0x08, 0x00, 0x00, 0x00, 0x00 ],
					[ 0x12, 0x00, 0x00, 0x00, 0x00 ],
					[ 0x14, 0x00, 0x00, 0x00, 0x00 ],
				]
			),
			cc_el: ([ 0x00, 0x00, 0x00, 0x00, 0x00 ]),
		}
	}
}

pub struct CCDataMutex {
	ccdata_mutex: Mutex<CCData>,
} 

impl CCDataMutex {
	// Create a new instance and initialise the Mutex
	pub fn new() -> CCDataMutex {
		CCDataMutex {
			ccdata_mutex: Mutex::new(CCData::new()),
		}
	}

	// Return the next CC data in sequence
	pub fn cc_out_next_seq(&mut self) -> [u8; 5] {
		
		let mut m = self.ccdata_mutex.lock().unwrap();
		m.cc_el = m.cc_array[m.cc_idx];
		m.cc_idx = m.cc_idx + 1;
		if m.cc_idx > RR_CC {
			m.cc_idx = 0;
		}
		return m.cc_el.clone();
	}
}

Yes.