How to select trait implementation by return type?

I implemented a Reader which is able to read several primitive type from a source. In order to not have lots of methods called read_u8, read_string , read_slice_vec_u8, read_foo I used several implementations for a custom Read-trait.

Most of the time the compiler can deduce the right implementation, but sometimes I have to help a bit by inferring a type.

trait Read<T> {
    fn read(&self) -> T;
}

struct Reader {}

impl Read<u32> for Reader {
    fn read(&self) -> u32 {
        println!("read::u32");
        0
    }
}

impl Read<usize> for Reader {
    fn read(&self) -> usize {
        println!("read::usize");
        1
    }
}

fn main() {
    let reader = Reader {};

    let a: u32 = reader.read();
    let b: usize = reader.read();
    println!("u32: {}", a);
    println!("usize: {}", b);

    //println!("u32: {}", reader.read() as u32);
    //println!("usize: {}", reader.read() as usize);

    //println!("u32: {}", (reader as Read<u32>).read());
    //println!("usize: {}", (reader as Read<usize>).read());
    
    //println!("u32: {}", reader.read::<u32>());
    //println!("usize: {}", reader.read::<usize>());
}

I fail to find the right notation for that. The only variant that actually worked was using a temporary binding which is rather cumbersome.

trait Read<T> {
    fn read(&self) -> T;
}

struct Reader {}

impl Reader {
    pub fn read_a<T>(&self) -> T
    where Self: Read<T> {
        self.read()
    }
}

impl Read<u32> for Reader {
    fn read(&self) -> u32 {
        println!("read::u32");
        0
    }
}

impl Read<usize> for Reader {
    fn read(&self) -> usize {
        println!("read::usize");
        1
    }
}

fn main() {
    let reader = Reader {};

    let a: u32 = reader.read();
    let b: usize = reader.read();
    println!("u32: {}", a);
    println!("usize: {}", b);

    println!("u32: {}", reader.read_a::<u32>());
    println!("usize: {}", reader.read_a::<usize>());
}

The major problem with this approach is that you can't define a read_an method that only works with type parameters that begin with a vowel sound.

1 Like

Thanks again for you quick help!

So the problem was that there was no place in the function's signature where the caller could place the type annotation?

Playing with your solution I found that the type argument is even optional if it can be inferred:

trait Read<T> {
    fn read_primitive(&self) -> T;
}

struct Reader {}

impl Reader {
    pub fn read<T>(&self) -> T where Self: Read<T> {
        self.read_primitive()
    }
}

impl Read<u32> for Reader {
    fn read_primitive(&self) -> u32 {
        println!("read::u32");
        0
    }
}

impl Read<usize> for Reader {
    fn read_primitive(&self) -> usize {
        println!("read::usize");
        1
    }
}

fn main() {
    let reader = Reader {};

    let a: u32 = reader.read();
    let b: usize = reader.read();
    println!("u32: {}", a);
    println!("usize: {}", b);
    
    println!("u32: {}", reader.read::<u32>());
    println!("usize: {}", reader.read::<usize>());
}

(This version also solves the a vs. an problem :wink: )