Implementing trait for a concrete type parameter


#1

I have a struct I’m using for serde serialization:

pub struct CdrSerializer<W> where W: Write {
    pub endianness: CdrEndianness,
    pub write_handle: W
}

and in some scenarios (mainly testing) I want to use a std::io::Cursor value for the write_handle:

let new_slice = &mut buf[..];
let writeable_buf = Cursor::new(new_slice);

let mut serializer = CdrSerializer{
    endianness: CdrEndianness::Big,
    write_handle: writeable_buf
};

now the big thing is I’d like to get the position on the cursor. how can I keep the generic code but add a position if I know the type parameter is std::io::Cursor?

I’ve tried this:

pub trait Positionable {
    fn position(&self) -> usize;
}

impl Positionable for CdrSerializer<Cursor> {
    fn position(&self) -> usize {
        100
    }
}

but the errors are hard to tease apart:

error[E0243]: wrong number of type arguments
  --> src/cdr/ser.rs:61:37
   |
61 | impl Positionable for CdrSerializer<Cursor> {
   |                                     ^^^^^^ expected 1 type arguments, found 0

Is this a syntax problem or a conceptual problem?


#2

You’re actually pretty close! The issue is that Cursor<T> is itself a generic type, so you need to be able to provide a type bound for it.

Something like this should work:

impl <T> Positionable for CdrSerializer<Cursor<T>>

Looking at the docs for Cursor it looks like a good bound for that T above could be AsRef<[u8]>, if you need to work with bytes in the position method. So if that was the case your impl would look like:

impl <T: AsRef<[u8]>> Positionable for CdrSerializer<Cursor<T>>

#3

Ah, great insight! That got me unblocked. But then I realized I had the CdrSerializer in scope and the write_handle property could be accessed by its concrete type – so I didn’t even need the Positionable trait!

For some reason I thought once assigned to the write_handle property the value could be accessed as through ‘Write’ interface.


#4

Ah gotcha :+1: Rust has decent inference on its generics, so in a lot of cases they’re omitted, but still there, quietly making the world a better place. So if we put the generics back in your above sample, you can see why you can access your write_handle as more than just Write:

let mut serializer: CdrSerializer<Cursor<&mut [u8]>> = CdrSerializer { ... }

let cursor: Cursor<&mut [u8]> = serializer.write_handle;

let position = cursor.position;

#5

To b[quote=“KodrAus, post:4, topic:7438”]
let cursor: Cursor<&mut [u8]> = serializer.write_handle;
[/quote]

To be clear, this bit could panic if the write_handle was a different type, correct? What’s a good way to get get a Option type?


#6

It’ll actually be a compile error, because the types are known at compile-time. This is the best possible outcome because it means if that code is running it’ll always work as advertised.

I’ve put together a quick playground to show you.