In my case, it seems like I need a trait that allows me to go from the owned type to the (usual) borrowed type.
I finally ended up making my own type for that:
pub trait BorrowStorable
where
Self: Sized + Borrow<Self::Stored> + Ord,
{
type Stored: ?Sized + StorableWithOwned<Self>;
}
impl<T> BorrowStorable for T
where
T: StorableWithOwned<T>,
{
type Stored = Self;
}
impl<T> BorrowStorable for Vec<T>
where
T: Ord,
[T]: StorableWithOwned<Vec<T>>,
{
type Stored = [T];
}
impl BorrowStorable for String {
type Stored = str;
}
Which I can then use like this:
unsafe impl<T1, T2> Storable for (T1, T2)
where
- T1: StorableWithOwned<T1> + StorableConstBytesLen,
- T2: StorableWithOwned<T2>,
+ T1: BorrowStorable,
+ T2: BorrowStorable,
+ <T1 as BorrowStorable>::Stored: StorableConstBytesLen,
{
- const CONST_BYTES_LEN: bool = T2::CONST_BYTES_LEN;
+ const CONST_BYTES_LEN: bool = <T2 as BorrowStorable>::Stored::CONST_BYTES_LEN;
type AlignedRef<'a> = Owned<Self>;
type BytesRef<'a> = Vec<u8>
where
T1: 'a,
T2: 'a;
fn to_bytes(&self) -> Self::BytesRef<'_> {
- let mut bytes = Vec::with_capacity(self.0.bytes_len() + self.1.bytes_len());
- bytes.extend_from_slice(&self.0.to_bytes());
- bytes.extend_from_slice(&self.1.to_bytes());
+ let mut bytes = Vec::with_capacity(self.0.borrow().bytes_len() + self.1.borrow().bytes_len());
+ bytes.extend_from_slice(&self.0.borrow().to_bytes());
+ bytes.extend_from_slice(&self.1.borrow().to_bytes());
bytes
}
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self::AlignedRef<'_> {
- let v1: T1 = T1::owned_from_bytes_unchecked(&bytes[0..T1::BYTES_LEN]);
- let v2: T2 = T2::owned_from_bytes_unchecked(&bytes[T1::BYTES_LEN..]);
+ let v1: T1 = <T1 as BorrowStorable>::Stored::owned_from_bytes_unchecked(&bytes[0..<T1 as BorrowStorable>::Stored::BYTES_LEN]);
+ let v2: T2 = <T2 as BorrowStorable>::Stored::owned_from_bytes_unchecked(&bytes[<T1 as BorrowStorable>::Stored::BYTES_LEN..]);
Owned((v1, v2))
}
}
Such that even if Vec<u8>
is not Storable
(you're supposed to use [u8]
), I still have a (u64, Vec<u8>)
being Storable
.
But not sure if all that makes sense. I'm still working on it. I just shared it here to point out what was my original go: Have a trait that serves as a type constructor which goes from String
to str
(where I could use Deref
maybe?) but also from i32
to i32
(where I can't use Deref
), i.e. from an owned type to the immutable borrowed type.
StorableWithOwned
, I defined like this:
pub trait StorableWithOwned<T>: Storable {
/// Converts from byte slice into (owned) `Self`
unsafe fn owned_from_bytes_unchecked(bytes: &[u8]) -> T;
}
impl<T, O> StorableWithOwned<O> for T
where
T: ?Sized + Storable,
for<'a> <<T as Storable>::AlignedRef<'a> as PointerIntoOwned>::Owned: IsType<O>,
{
unsafe fn owned_from_bytes_unchecked(bytes: &[u8]) -> O {
Self::from_bytes_unchecked(bytes).into_owned().identity()
}
}
trait IsType<T> {
/// Returns value with its type being same as the type argument
fn identity(self) -> T;
}
impl<T> IsType<T> for T {
fn identity(self) -> T {
self
}
}
Btw, I wonder if my IsType
helper trait is something that's also commonly needed in other contexts. It's very generic actually, but I suppose we don't have something like that in std
?