How to implement bitwise operations for generic trait types?

I'm trying to implement a trait to access CPU registers and get/set their distinct fields.

pub trait ReadWrite<T: Copy> {

    // #[inline]
    fn get(&self) -> T;

    // #[inline]
    fn set(&self, value: T);

    #[inline]
    fn get_field(&self, field_attr: (u8, u8)) -> T {
        let mask = ((1 << field_attr.1) - 1) << field_attr.0;
        (self.get() & mask) >> field_attr.0
    }
}

And I define registers as

pub struct MpidrEl1;

pub mod mpidr_el1 {

    pub enum Field {
        Aff0,
        Aff1,
        Aff2,
        MT,
        U,
        Aff3
    }
}

impl ReadWrite<u64> for MpidrEl1 {

    #[inline]
    fn get(&self) -> u64 {
        let r: u64;
        unsafe {
            asm!("mrs $0, mpidr_el1":"=r"(r):::"volatile")
        }
        r
    }

    #[inline]
    fn set(&self, value: u64) {
        unsafe {
            asm!("msr mpidr_el1, $0"::"r"(value)::"volatile")
        }
    }

    fn field_attr(field: mpidr_el1::Field) -> (u8, u8) {
        match field {
            mpidr_el1::Field::Aff0 => (0, 8),
            mpidr_el1::Field::Aff1 => (8, 8),
            mpidr_el1::Field::Aff2 => (16, 8),
            mpidr_el1::Field::MT => (24, 1),
            mpidr_el1::Field::U => (30, 1),
            mpidr_el1::Field::Aff3 => (32, 8)
        }
    }
}

First, when I tried to complile I was getting an error:

   |
33 |         (self.get() & mask) >> field_attr.0
   |          ---------- ^ ---- {integer}
   |          |
   |          T
   |
   = note: `T` might need a bound for `std::ops::BitAnd`

Okay, I added

use core::ops::BitAnd;

pub trait ReadWrite<T: Copy + BitAnd> {
    // . . .
}

This has caused the next errors:

error[E0308]: mismatched types
  --> lib/register/src/cpu.rs:33:23
   |
33 |         (self.get() & mask) >> field_attr.0
   |                       ^^^^ expected type parameter, found integer
   |
   = note: expected type `T`
              found type `{integer}`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

error[E0369]: binary operation `>>` cannot be applied to type `<T as core::ops::BitAnd>::Output`
  --> lib/register/src/cpu.rs:33:29
   |
33 |         (self.get() & mask) >> field_attr.0
   |         ------------------- ^^ ------------ u8
   |         |
   |         <T as core::ops::BitAnd>::Output
   |
   = note: an implementation of `std::ops::Shr` might be missing for `<T as core::ops::BitAnd>::Output`

Adding core::ops::{Shl, Shr} didn't help. Error says:

binary operation `>>` cannot be applied to type `<T as core::ops::BitAnd>::Output`

Well, I don't understand where it takes me and how to resolve it. I tought integer types implement all that bitwise operations by default. But I guess the problem is that T generic is not treated as integer only type. Could you please explain why it doesn't work and the proper way of doing such things?
Thanks in advance!

Each binary operator traits can be parameterized by all three types: operand types (left and right-hand sides) and the result type.
For example, L: Shl<R, Output = O> means l << r has type O for l of type L and r of type R. R can be omitted and defaults to L.

In (self.get() & mask), mask has undetermined integer literal type. & operator cannot be applied to this and T. Probably what you want is to make mask to be the same type as T. To represent this generically, let's add T: From<u8> and rewrite all literal 1s to T::from(1). Now, add the T: BitAnd<Output = T> bound to calculate & operator. Don't forget to specify the Output type.
Now, you have to add bounds for the shift operators (left shift and right shift). Notice that the right-hand sides of the shift operations are type u8. What bounds you have to specify?

1 Like

Thank you very much for the explanation!
I changed the trait

use core::ops::{BitAnd, Shl, Shr, Sub};

pub trait ReadWrite<T>
where T:
    Copy +
    From<u8> +
    BitAnd<Output = T> +
    Shl<Output = T> +
    Shr<Output = T> +
    Sub<Output = T>
{

    // #[inline]
    fn get(&self) -> T;

    // #[inline]
    fn set(&self, value: T);

    #[inline]
    fn get_field(&self, field_attr: (u8, u8)) -> T {
        let field_start = T::from(field_attr.0);
        let field_size = T::from(field_attr.1);
        let mask = ((T::from(1) << field_size) - T::from(1)) << field_start;
        (self.get() & mask) >> field_start
    }
}

and it compiles now