Generics issue; Need to change a T type in all my program, will everywhere be the same, I promise!

Hi,

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());

Thanks!

You could bound T and U using a combination of traits from core::ops and num_traits, like so:

use core::ops;
use num_traits::AsPrimitive;

impl <T, U> Computer<T, U>
where T: ops::Add<Output = T> + Copy + 'static,
      U: AsPrimitive<usize> + ops::AddAssign<U> + Copy + 'static,
      u8: AsPrimitive<T> + AsPrimitive<U>,
{
    pub fn new(code: Vec<String>) -> Computer<T, U> {
        Computer {
            regA: 0u8.as_(),
            regB: 0u8.as_(),
            regStatus: 0,
            regPC: 0u8.as_(),
            code,
        }
    }

    pub fn next_step(&mut self) {
        let ins: &str = self.code.get(self.regPC.as_()).unwrap_or(&"EOL".to_string());
        // do sth with ins
        self.regPC += 1.as_();
    }

    fn add_a_b(&mut self) {
        self.regA = self.regA + self.regB;
    }
}

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;
    }
}
2 Likes

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.

2 Likes

Very thanks you a lot!! Also by the syntaxis example <3

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.