I'm attempting to make some types that that were originally defined as a File
-only wrappers, be generic over the actual extension trait they use (PosRead
) and also generic over different Borrow
types. I have it compiling, tests and clippy passing, but I'm not sure if its the best way to do it, either for my code hygiene or ergonomics.
To explain the Borrow
aspect better, I have a type ReadSlice
that I want to be able to construct with an owned value like ReadSlice<File, File>
or to use a shared reference: ReadSlice<File, &File>
or ReadSlice<File, Arc<File>>
, where in these cases File
implements the PosRead
trait.
Here is my current definition:
#[derive(Debug)]
pub struct ReadSlice<P, B>
where P: PosRead, B: Borrow<P>
{
start: u64,
pos: u64,
end: u64,
file: B,
phantom: PhantomData<fn() -> P>
}
Firstly, there were various problems with anything short of both generic parameters. When introducing B: Borrow<P>
however, the compiler complains of an unused P
unless the PhantomData
is also included. That was somewhat surprising to me because the B
parameter is used and defined in terms of P
. Why isn't that sufficient?
Next, PhantomData rustdoc seems to suggest I use PhantomData<*const P>
"so as not to indicate ownership". I'm not hiding a raw pointer here, an the file
field and Borrow
type should already deal with the ownership/lifetime aspects, correct? However with *const P
I loose Sync/Send for the type, which is undesirable.
I then found this fiendish table in the Nomicon, which lead me to the PhantomData<fn() -> P>
form not mentioned in the rustdoc. I'm not entirely sure if I want to preserve variance over P, and I didn't find a case where I ran afoul of Drop Check when just using PhantomData<P>
. Is there something better I should be using here?
Finally, when constructed this type the compiler can't infer P from the B type as passed? I end up needing to construct like this, for all but the owned File
type:
let rslice = ReadSlice::<File, _>::new(Arc::new(file), 0, length);
I tried to use a default generic parameter, e.g:
pub struct ReadSlice<P, B=Borrow<P>>
where P: PosRead, B: Borrow<P>
…which caused another problem instead of helping. In retrospect, this isn't likely the way to get the compiler to infer the P
type from B
. Is there another? Its not terrible now, but is there some way to make this more ergonomic?
The full work-in-progress is at: dekellum/olio#1.
Original release was previously posted as New crate: olio.