Hi,
I think it would be very useful to have a few intrinsics for doing specialisation as below. The idea is that during monomorphization any type conversion that can be statically verified will be cast to the target type or None
otherwise. The functions below avoid lifetime changes, so I think they should avoid the soundness issues currently blocking specialisation. I believe this could just use the current type checking and casting under the hood.
The specific functions I have in mind are:
const fn try_cast<T: 'static + ?Sized, U: 'static + ?Sized>(that: T) -> Option<U>;
const fn try_cast_ref<T: 'static + ?Sized, U: 'static + ?Sized>(that: &T) -> Option<&U>;
const fn try_cast_mut<T: 'static + ?Sized, U: 'static + ?Sized>(that: &mut T) -> Option<&mut U>;
It could be useful to have a Result
return type instead to be able to know why a cast failed.
These functions don't have to be intrinsics, that's just where they seemed to make sense to live.
There are many ways they could be used, but the specific case I have in mind is situations like this (contrived):
fn compute<T: Compute>(that: &T) -> usize {
if let Some(cheaper) = try_cast_ref::<dyn CanComputeCheap>(that) {
cheaper.compute_cheaply()
} else if let Some(known) = try_cast_ref::<[Cheap]>(that) {
known.map(Cheap::compute_cheaply).sum()
} else {
that.compute()
}
}
After monomorphization and optimisation I expect that the code could become something like these:
fn compute(that: &Cheap) -> usize {
that.compute_cheaply()
}
fn compute(that: &[Cheap]) -> usize {
known.map(Cheap::compute_cheaply).sum()
}
fn compute<T: Compute>(that: &T) -> usize {
that.compute()
}