Hi all! Working on a crate that uses LMDB as a key-value store (also planned to become open-source eventually), I have the following (unsafe) trait:
/// Types that can be stored
pub unsafe trait Storable: Ord {
/// Is type fixed in size? Must be set to true if (and only if) type is Sized
const FIXED_SIZE: bool;
/// Is type equivalent to [`c_uint`] or [`c_size_t`]?
const OPTIMIZE_INT: bool = false;
/// Is [`Ord`] consistent with lexicographical sorting of binary representation?
const TRIVIAL_CMP: bool = false;
/// Pointer to aligned version of Self
type AlignedRef<'a>: Deref<Target = Self>;
/// Pointer to byte representation
type BytesRef<'a>: Deref<Target = [u8]>
where
Self: 'a;
/// Converts to byte slice
fn to_bytes(&self) -> Self::BytesRef<'_>;
/// Converts from byte slice
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self::AlignedRef<'_>;
/// Compares byte representation
unsafe fn cmp_bytes_unchecked(a: &[u8], b: &[u8]) -> cmp::Ordering {
Self::from_bytes_unchecked(a).cmp(&Self::from_bytes_unchecked(b))
}
}
I currently have to manually set the FIXED_SIZE
constant for every (unsafe) implementation of the trait, according to whether the type has a fixed size or not.
The constant is later used here:
#[derive(Clone, Copy, Debug)]
pub struct DbOptions<K: ?Sized, V: ?Sized, C> {
key: PhantomData<K>,
value: PhantomData<V>,
constraint: PhantomData<C>,
lmdb_flags: LmdbFlags,
}
impl<K: ?Sized, V: ?Sized, C> DbOptions<K, V, C> {
/// Set stored value type
pub const unsafe fn value_type<T>(mut self) -> DbOptions<K, T, C>
where
T: ?Sized + Storable,
{
flag_set!(self, MDB_DUPFIXED, T::FIXED_SIZE); // <- using it here!!
flag_set!(self, MDB_INTEGERDUP, T::OPTIMIZE_INT);
DbOptions {
value: PhantomData,
..self
}
}
/* … */
}
Now I wonder if it's possible to automatically set that constant or use a const fn
to evaluate whether the type is sized or not.
We already have std::mem::size_of
, which returns the size (but it's only defined for Sized
types). What I'm missing is something like
const fn try_size_of<T: ?Sized>() -> Option<usize>;
which would return None
for types that are !Sized
. Is it possible to write such a (const) function?
Then I could write:
flag_set!(self, MDB_DUPFIXED, try_size_of::<T>.is_some());