I'm trying to build a library for structural typing in rust, and I want to behave a certain way if two generic parameters refer to the same type. I would like this to work correctly for any type. This is a simplified version of one of my functions:
fn am_i_safe<A, B>(a: A, b: B) -> B {
if core::intrinsics::type_id<A>() == core::intrinsics::type_id<B>() {
let a_as_b = unsafe { std::mem::transmute_copy::<A, B>(&a) };
std::mem::forget(a);
a_as_b
} else {
b
}
}
Is this safe? can this ever cause any type of undefined behavior or memory safety issue?
Thanks for any help I'm excited to learn more about what's possible with Rust
Here's an allocation-free safe version. A general rule is: when you want to go from (mutable) reference to value, use Option::take.
fn am_i_safe<A: 'static, B: 'static>(a: A, b: B) -> B {
let mut a = Some(a);
let a: &mut dyn Any = &mut a;
a.downcast_mut()
.and_then(Option::take)
.unwrap_or(b)
}
Thanks to both of you!
One of the reasons I was using core::intrinsics is that the functionality of Any isn't const, and I'd really like for this branch to be optimized away by the compiler (because in practice I'm calling this function many times recursively). Maybe I can leave the core::intrinsics check in (until comparing type ids is const) but use downcasting instead of transmute_copy, just in case I'm doing it wrong. Does that sound reasonable? Alternatively - do you know if the type comparisons in core::any happen at compile time even though they aren't const? I feel like those aren't necessarily the same thing
Const doesn't affect optimizations. It's a contract that keeps the function author from writing code that can't be evaluated at compile time. (The part of the compiler that checks whether something is const-eligible could be used for checking whether something can be optimized at compile time and vice versa – this is probably not actually implemented exactly like that, but it shows that the two problems are of the same difficulty.) Thus, the branch will almost certainly be optimized out even for Any.
I am not an assembly expert, but I took a look at the release build asm output of the Option playground. The "fallback" string doesn't seem to be included in the final program. So at least in this case it seems likely that the compiler is seeing through the indirection.