Hello!
This is a really odd request/question.
I need to go from a given generic T to a generic T: MyTrait, so I want to convert it to a type impl MyTrait basically. Making the generic T have a requirement to be MyTrait also isn't really possible...
So I wonder.. from compiler perspective, how hard/impossible would something like this be:
impl Trait for Special {
fn magic<T>(self, val: T) {
// I know that T always implements MyTrait
let m(: impl MyTrait) = unsafe { typed_downcast(val) };
m.run_function();
}
}
I think this could work because the compiler should (from my understanding, which is not much) be able to just handle it as written down for pre-monomorphization and then error when a type doesn't implement that post monomorphization. Right?
The OP said to "error when a type doesn't implement that post monomorphization". "error" does not sound like "UB" to me. But if UB is desired, that's easy to do by just putting hint::unreachable_unchecked() in the "else" branch of a "does the type implement the trait" check.
By wanting to specialize on whether a type implements a trait or not during monomorphization, you are asking for a form of specialization, whether you like it or not.
Yeah thus I like calling it „hinting“ more :> I’ve been thinking about if I could do this in stable and have one idea which would be completely unsafe but could work, need to try it tho
Along those lines, sometimes because of rust bugs with higher ranked bounds it just fails to see that the type implements the trait, would be good to force it sometimes.
I thought about the API a bit and I think something like this makes the most sense considering current language "limitations" (e.g. no generic traits):
impl Trait for Special {
fn magic<T>(self, val: T) {
unsafe {
bind_type!(type MagicT = T + MyTrait); // Declares a new type which "extends" from T and implements MyTrait
// That step is unsafe as one could bypass generic bounds on a function (like TypeId::of's T has a bound of 'static
}
let m(: impl MyTrait) = unsafe { ::core::mem::transmute::<_, MagicT>(val) };
m.run_function();
}
}
The MagicT would otherwise have all other characteristics from T, like it's lifetime and all generics.
transmute_unchecked isn't intended to ever be exposed directly — it causes ICEs and/or codegen errors if the size isn't equal, not just UB — but maybe a similar function is worth providing, for the purpose of doing by-value type system crimes.
I know that I've used a helper like this multiple times:
/// Like `transmute`, but for generic code.
///
/// If `Src` is smaller than `Dst`, errors when monomorphized.
/// If `Src` is larger than `Dst`, the byte prefix of `src` is transmuted.
///
/// # Safety
///
/// The first `size_of::<Dst>()` bytes of `src` must be a valid `Dst`.
pub unsafe fn transmute_prefix<Src, Dst>(src: Src) -> Dst {
const { assert!(size_of::<Dst>() <= size_of::<Src>()) };
unsafe { transmute_copy(&ManuallyDrop::new(src)) }
}
Whut? I’m proposing normal transmute here, which the user should use themselves, as the compiler should infer that both types have the same size (being the same underlying type, just with another trait bound)
huh? No the problem is that I need to name the Dst type That's what I am proposing here all along, somehow making rust aware of a type which is basically another already given type but just hinting it having another trait bound
I don’t think a new type is what you want here? You want it to be usable in all the places the existing type is.
I think one solution would be the ability to specify bounds that are assumed to hold when type-checking the inside of a function, but aren't checked when calling the function.
Yeah I get that, but I feel like that's hard to integrate in current rust. Like best I could imagine is making a macro which wraps around such an "area"