I would like to write a trait or function that:
- Uses stable Rust
- Consumes its arguments (i.e. by value)
- Is specialized when its arguments are the same type
My motivation is to write a function that maps from one ndarray
::ArrayBase
to another array of the same shape, but of a possibly different underlying type. If the input and output types are the same then I would like to call ArrayBase::mapv_into()
to avoid extra allocations.
To my knowledge, it is not possible do this at compile time in stable rust without some form of specialization. But it looks like I can do dynamic specialization/dispatch at runtime with nominal overhead (playground):
use core::any::TypeId;
use core::mem::transmute;
use core::ptr;
// Some types.
struct A { }
struct B { }
// Do something really efficient if arguments are the same type.
fn same<T>(_t1: T, _t2: T) {
println!("same");
}
// Do something more generically if arguments could be of different types.
fn diff<T,U>(_t: T, _u: U) {
println!("different");
}
// My attempt at runtime specialization. Is this `unsafe` kosher?
fn any<T: 'static, U: 'static>(t: T, u: U) {
// Dispatch to the specialized `same()` if arguments are the same type.
if TypeId::of::<T>() == TypeId::of::<U>() {
// Transmute from U to T.
let t1 = t;
let t2 = unsafe {
let u_ptr: *const U = &u;
let t_ptr = transmute::<*const U, *const T>(u_ptr);
let t2 = ptr::read(t_ptr);
core::mem::forget(u); // EDIT forgot to forget() in original post
t2
};
same(t1,t2)
}
// Dispatch to the default `diff()` otherwise.
else {
diff(t,u)
}
}
fn main() {
any(A{}, A{}); // same
any(A{}, B{}); // different
}
My question is:
- Is the above use of
unsafe
safe? - Is there a better way to do this without
unsafe
?
I think I could do this with safe code using Any::downcast_ref()
if my function took its arguments by reference (i.e. fn any<T: Any, U: Any>(&dyn T, &dyn U)
), but I cannot seem to figure out how to take the arguments by value using safe code.