In the real program y
is calculated to change the index pointing into a 64K array , and wraparound is the desired behavior)
There is no consensus of how things should be done, but here's my advice: Provide to the user two functions:
fn add(&mut self, index: u16) -> u16 { // return previous index why not
// ..
}
fn sub(&mut self, index: u16) -> u16 {
// ..
}
You could also add a helper function that should not be used lightly:
fn offset(&mut self, offset: i16) -> u16 {
// ..
}
The purpose is that the user should know whenever use sub or add, the user should manage only unsigned type. That question is opinion oriented so I understand if people don't agree.
Full example:
use std::mem;
#[derive(Debug, PartialEq, PartialOrd)]
struct MyIndex {
index: u16,
}
impl MyIndex {
fn new(index: u16) -> Self {
Self { index }
}
fn add(&mut self, index: u16) -> u16 {
let index = self.index.wrapping_add(index);
self.replace(index)
}
fn sub(&mut self, index: u16) -> u16 {
let index = self.index.wrapping_sub(index);
self.replace(index)
}
fn offset(&mut self, offset: i16) -> u16 {
if offset > 0 {
self.add(offset as u16)
} else {
self.sub(offset as u16)
}
}
fn replace(&mut self, index: u16) -> u16 {
mem::replace(&mut self.index, index)
}
}
fn main() {
let mut index = MyIndex::new(42);
let mut other_index = MyIndex::new(84);
let (x, first) = if index > other_index {
(index.index - other_index.index, true)
}
else {
(other_index.index - index.index, false)
};
// ...
if first {
index.sub(x);
}
else {
other_index.sub(x);
}
println!("{:?} {:?}", index, other_index);
index.sub(21);
println!("{:?}", index);
index.offset(-1);
println!("{:?}", index);
}