I'm having some troubles with the generic types on my code. I need to emulate a computer which registers' types could change as a config.toml file says. Here is what I've been trying.
struct Computer<T, U> {
regA: T,
regB: T,
regStatus: u8,
regPC: U,
code: Vec<String>
}
impl <T, U> Computer<T, U> {
//! T: allways: u8, i8, u16, i16, u32, i32, u64 or i64.
//! U: allways: u8, u16, u32, u64. Will cast to usize.
pub fn new(code: Vec<String>) -> Computer<T, U> {
Computer {
regA: 0 as T,
regB: 0 as T,
regStatus: 0,
regPC: 0 as U,
code,
}
}
pub fn next_step(&mut self) {
let ins: &str = self.code.get(self.regPC as usize).unwrap_or(&"EOL".to_string());
// do sth with ins
self.regPC += 1;
}
fn add_a_b(&mut self) {
self.regA = self.regA + self.regB;
}
}
Probably I should define T more like a Wrapper (where type is u8, i8, ...).
My intention is to call this:
let mut computer = Computer::new::<u8, u8>(my_code.clone());
// or
let mut computer = Computer::new::<i8, u8>(my_code.clone());
// or
let mut computer = Computer::new<i8, u32>(my_code.clone());
If youβll know the types at compile time and donβt need to have multiple instances with different types in the same program, you could use type aliases and conditional compilation instead of making the structure generic:
#[cfg(feature=βt_u8β)] type T=u8;
#[cfg(feature=βt_i8β)] type T=i8;
#[cfg(feature=βt_u16β)] type T=u16;
#[cfg(feature=βt_i16β)] type T=i16;
#[cfg(feature=βt_u32β)] type T=u32;
#[cfg(feature=βt_i32β)] type T=i32;
#[cfg(feature=βt_u64β)] type T=u64;
#[cfg(feature=βt_i64β)] type T=i64;
#[cfg(feature=βu_u8β)] type U=u8;
#[cfg(feature=βu_u16β)] type U=u16;
#[cfg(feature=βu_u32β)] type U=u32;
#[cfg(feature=βu_u64β)] type U=u64;
struct Computer {
regA: T,
regB: T,
regStatus: u8,
regPC: U,
code: Vec<String>
}
impl Computer {
pub fn new(code: Vec<String>) -> Computer {
Computer {
regA: 0 as T,
regB: 0 as T,
regStatus: 0,
regPC: 0 as U,
code,
}
}
pub fn next_step(&mut self) {
let ins: &str = self.code.get(self.regPC as usize).unwrap_or(&"EOL".to_string());
// do sth with ins
self.regPC += 1;
}
fn add_a_b(&mut self) {
self.regA = self.regA + self.regB;
}
}
Sadly, this is impossible to explain to the compiler. It will behave as if T could be a String, an array of 83 file handles, a socket, or whatever other type could exist.
You can add bounds to require T types to support certain operations, but it's still always going to be an open set that could in theory allow any new type as long as that type adds implementations for the required traits.