Rust claims the newtype pattern has no runtime overhead. However, I ran into cases where the statement seems not correct. Consider the following Rust program:
pub struct MyInt (i32);
pub fn f(xs: [i32; 3]) -> i32 {
xs[0]
}
pub fn g() -> i32 {
let xs = [MyInt(1), MyInt(2), MyInt(3)];
let myxs = xs.map(|x| x.0);
f(myxs)
}
Given an API like the function f
, which takes an array of i32
, I have to map over my array of MyInt
in order to call it. If I understand it correctly, this will create a new array, which seems not to be "runtime overhead free". Did I miss anything? Or is this considered as a misuse of the new type pattern?
I'd appreciate your help.
I didn't find much discussion on the topic of a newtype used in an array or other containers.
edit:
After reading the comments, I realized that the problem is more about array rather than the newtype. However, if I use the slice, to the best of my knowledge, I would need the following program
pub struct MyInt (i32);
pub fn f(xs: &[i32]) -> i32 { xs[0] }
pub fn g() -> i32 {
let xs = [MyInt(1), MyInt(2), MyInt(3)].as_slice();
let myxs: &[i32] = unsafe { std::mem::transmute(xs) };
f(myxs)
}
or with vectors
pub struct MyInt (i32);
pub fn f(xs: Vec<i32>) -> i32 { xs[0] }
pub fn g() -> i32 {
let xs = vec![MyInt(1), MyInt(2), MyInt(3)];
let myxs: Vec<i32> = xs.into_iter().map(|MyInt(x)| x).collect();
f(myxs)
}
However, it seems I can't get away with the unsafe transmute
in the array version and I still have to pay for the cost of creating a new vector in the vector version. Are there any better approaches?
Transmuting slices or Vec
is only safe if the newtype is marked #[repr(transparent)]
(or #[repr(C)]
). If it is, you can use bytemuck
to do it safely:
#[derive(Clone, Copy, bytemuck::NoUninit)]
#[repr(transparent)]
pub struct MyInt(i32);
pub fn f(xs: &[i32]) -> i32 { xs[0] }
pub fn slice() -> i32 {
let xs = [MyInt(1), MyInt(2), MyInt(3)];
let myxs: &[i32] = bytemuck::cast_slice(&xs);
f(myxs)
}
pub fn vec() -> i32 {
let xs = vec![MyInt(1), MyInt(2), MyInt(3)];
let myxs: Vec<i32> = bytemuck::cast_vec(xs);
f(&myxs)
}