Is there a way in Rust to overload method for a specific type?

The following is only an example. If there's a native solution for this exact problem with reading bytes - cool, but my goal is to learn how to do it by myself, for any other purpose as well.

I'd like to do something like this: (pseudo-code below)

let mut reader = Reader::new(bytesArr);
let int32: i32 = reader.read(); // separate implementation to read 4 bits and convert into int32
let int64: i64 = reader.read(); // separate implementation to read 8 bits and convert into int64

I imagine it looking like this: (pseudo-code again)

impl Reader {
    read<T>(&mut self) -> T {
        // if T is i32 ... else if ...
    }
}

or like this:

impl Reader {
    read(&mut self) -> i32 {
        // ...
    }
    read(&mut self) -> i64 {
        // ...
    }
}

But haven't found anything relatable yet.
(I actually have, for the first case (if T is i32 ...), but it looked really unreadable and inconvenient)


You could do this by having a Readable trait which you implement on i32 and i64, which does the operation. Then on Reader you could have a generic function which takes any type that is Readable and return it, for example:

struct Reader {
    n: u8,
}

trait Readable {
    fn read_from_reader(reader: &mut Reader) -> Self;
}

impl Readable for i32 {
    fn read_from_reader(reader: &mut Reader) -> i32 {
        reader.n += 1;
        reader.n as i32
    }
}

impl Readable for i64 {
    fn read_from_reader(reader: &mut Reader) -> i64 {
        reader.n += 1;
        reader.n as i64
    }
}

impl Reader {
    fn read<T: Readable>(&mut self) -> T {
        T::read_from_reader(self)
    }
}

fn main() {
    let mut r = Reader { n: 0 };
    let int32: i32 = r.read();
    let int64: i64 = r.read();
    println!("{} {}", int32, int64);
}

You can try it on the playground


After some trials and searches, I found that implementing them in current Rust seems a bit difficult, but not impossible.

Here is the code, I'll explain it afterwards:

#![feature(generic_const_exprs)]

use std::{
    mem::{self, MaybeUninit},
    ptr,
};

static DATA: [u8; 8] = [
    u8::MAX,
    u8::MAX,
    u8::MAX,
    u8::MAX,
    u8::MAX,
    u8::MAX,
    u8::MAX,
    u8::MAX,
];

struct Reader;

impl Reader {
    fn read<T: Copy + Sized>(&self) -> T
    where
        [(); mem::size_of::<T>()]: ,
    {
        let mut buf = [unsafe { MaybeUninit::uninit().assume_init() }; mem::size_of::<T>()];
        unsafe {
            ptr::copy_nonoverlapping(DATA.as_ptr(), buf.as_mut_ptr(), buf.len());
            mem::transmute_copy(&buf)
        }
    }
}

fn main() {
    let reader = Reader;

    let v_u8: u8 = reader.read();
    dbg!(v_u8);

    let v_u16: u16 = reader.read();
    dbg!(v_u16);

    let v_u32: u32 = reader.read();
    dbg!(v_u32);

    let v_u64: u64 = reader.read();
    dbg!(v_u64);
}

Suppose the global static variable DATA is the target data you want to read.

In current Rust, we cannot directly use the size of a generic parameter as the length of an array. This does not work:

fn example<T: Copy + Sized>() {
    let mut _buf = [0_u8; mem::size_of::<T>()];
}

The compiler gives a weird error:

error: unconstrained generic constant
  --> src\main.rs:34:31
   |
34 |         let mut _buf = [0_u8; mem::size_of::<T>()];
   |                               ^^^^^^^^^^^^^^^^^^^
   |
   = help: try adding a `where` bound using this expression: `where [(); mem::size_of::<T>()]:`

There is an issue that is tracking it, if you want to go deeper into this error you can take a look.

We just follow the compiler's suggestion to add a where bound. This requires feature generic_const_exprs to be enabled.

Next, unsafe { MaybeUninit::uninit().assume_init() } is optional, which drops the overhead of initializing this array, since we will eventually overwrite it completely. You can replace it with 0_u8 if you don't like it.

Finally, copy the data you need and transmute this array to your generic type, return.

I think you will see the output you expect:

[src\main.rs:38] v_u8 = 255
[src\main.rs:41] v_u16 = 65535
[src\main.rs:44] v_u32 = 4294967295
[src\main.rs:47] v_u64 = 18446744073709551615