I recently ran into an issue where I wanted to use Any
for slices. However, it only allows 'static
types (based on what I read, this is because you get the same TypeId
regardless of lifetimes).
I came up with this workaround which I think is safe:
use std::{
any::{Any, TypeId},
marker::PhantomData,
};
#[derive(Clone, Debug)]
pub struct AnySlice<'a> {
tid: TypeId,
len: usize,
ptr: *const (),
marker: PhantomData<&'a ()>,
}
impl<'a> AnySlice<'a> {
pub fn from_slice(s: &'a [T]) -> Self {
Self {
len: s.len(),
ptr: s.as_ptr() as *const (),
tid: TypeId::of::(),
marker: PhantomData,
}
}
pub fn as_slice(&self) -> Option<&'a [T]> {
if TypeId::of::() != self.tid {
return None;
}
Some(unsafe { std::slice::from_raw_parts(self.ptr as *const T, self.len) })
}
pub fn is(&self) -> bool {
TypeId::of::() == self.tid
}
}
edit: Unfortunately it seems like Lemmy insists on mangling the code block. See the playground link below.
T: Any
ensures T
is also 'static
. The lifetime is preserved with PhantomData
. Here’s a playground link with some simple tests and a mut version: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3116a404c28317c46dbba6ed6824c8a9
It seems to pass Miri, including the mut version (which requires a bit more care to ensure there can only be one mutable reference). Any problems with doing this?
Then maybe publish it as a mini-crate (e.g.
any-slice
), ideally with a link to the discussion that proves that it is sound.