如何将Vec <Rgb <u8 >>转换为Vec <u8>

Tim*_*mmm 2 rust rust-piston

使用Piston image箱子,我可以通过喂它来写一个图像Vec<u8>,但我的实际数据是Vec<Rgb<u8>>(因为这更容易处理,我想动态增长它).

我怎样才能转换Vec<Rgb<u8>>Vec<u8>Rgb<u8>真的[u8; 3].这必须是unsafe转换吗?

Sve*_*ach 6

答案取决于您是否可以复制数据.如果复制不是您的问题,您可以这样做:

let img: Vec<Rgb<u8>> = ...;
let buf: Vec<u8> = img.iter().flat_map(|rgb| rgb.data.iter()).cloned().collect();
Run Code Online (Sandbox Code Playgroud)

但是,如果要在不复制的情况下执行转换,我们首先需要确保源和目标类型实际上具有相同的内存布局.Rust对结构的内存布局提供了很少的保证.它目前甚至不保证具有单个成员的结构与成员本身具有相同的内存布局.

在这种特殊情况下,Rust内存布局不相关,因为Rgb定义为

#[repr(C)]
pub struct Rgb<T: Primitive> {
    pub data: [T; 3],
}
Run Code Online (Sandbox Code Playgroud)

#[repr(C)]属性指定结构的内存布局应与等效的C结构相同.C标准中没有完全指定C内存布局,但根据不安全的代码指南,有一些规则适用于"大多数"平台:

  • 字段顺序保留.
  • 第一个字段从偏移量0开始.
  • 假设结构未打包,则每个字段的偏移量与该字段类型的ABI强制对齐对齐,可能会创建未使用的填充位.
  • 结构的总大小向上舍入到其整体对齐.

正如评论中指出的那样,C标准理论上允许在结构的末尾添加额外的填充.但是,活塞图像库本身假设一条通道数据与Rgb结构具有相同的内存布局,所以如果你在这个假设不成立的平台上,那么所有的赌注都是关闭的(我无法找到任何证据表明存在这样的平台).

Rust确保数组,切片和向量密集,并且结构和数组的对齐方式等于其元素的最大对齐方式.假设布局Rgb是由我在上面引用的规则所规定的,这保证了Rgb<u8>确实在内存中布置为三个连续的字节,这Vec<Rgb<u8>>确实是一个连续的,密集的RGB值缓冲区,所以我们的转换是安全.我们仍然需要使用不安全的代码来编写它:

let p = img.as_mut_ptr();
let len = img.len() * mem::size_of::<Rgb<u8>>();
let cap = img.capacity() * mem::size_of::<Rgb<u8>>();
mem::forget(img);
let buf: Vec<u8> = unsafe { Vec::from_raw_parts(p as *mut u8, len, cap) };
Run Code Online (Sandbox Code Playgroud)

如果你想防止在末尾有额外的填充Rgb,你可以检查是否size_of::<Rgb<u8>>()确实是3.如果是,你可以使用不安全的非复制版本,否则你必须使用上面的第一个版本.

  • 整个段落解释了为什么程序员认为这是安全的应该是直接在`unsafe`块旁边的注释. (3认同)