使用活塞image
板条箱,我可以通过向其馈送Vec<u8>
来写图像,但是我的实际数据是Vec<Rgb<u8>>
(因为这样更容易处理,并且我想动态地增长它。)
如何将Vec<Rgb<u8>>
转换为Vec<u8>
? Rgb<u8>
确实是[u8; 3]
。这是否必须是unsafe
转换?
答案 0 :(得分:6)
答案取决于您是否适合复制数据。如果复制对您来说不是问题,则可以执行以下操作:
let img: Vec<Rgb<u8>> = ...;
let buf: Vec<u8> = img.iter().flat_map(|rgb| rgb.data.iter()).cloned().collect();
但是,如果您要执行转换而不进行复制,那么我们首先需要确保您的源类型和目标类型实际上具有相同的内存布局。 Rust很少保证有关结构的内存布局。当前甚至不保证具有单个成员的结构具有与成员本身相同的内存布局。
在这种情况下,由于Rgb
is defined as
#[repr(C)]
pub struct Rgb<T: Primitive> {
pub data: [T; 3],
}
#[repr(C)]
属性指定该结构的内存布局应与等效的C结构相同。 C标准中并未完全指定C内存布局,但是根据unsafe code guidelines,对于“大多数”平台,有一些规则适用:
- 保留现场订单。
- 第一个字段从偏移量0开始。
- 假设未打包该结构,则每个字段的偏移量将与该字段类型的ABI强制对齐方式对齐,可能会创建未使用的填充位。
- 该结构的总大小四舍五入到其整体对齐方式。
正如注释中指出的那样,C标准理论上允许在结构末尾进行附加填充。但是,活塞图像库本身是makes the assumption that a slice of channel data has the same memory layout as the Rgb
struct,因此,如果您使用的平台不符合这一假设,那么所有的赌注都不会成立(而且我找不到任何证据表明该平台存在)。
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) };
如果您想避免在Rgb
末尾添加其他填充的情况,可以检查size_of::<Rgb<u8>>()
是否确实为3。如果是,则可以使用不安全的非复制版本,否则您必须使用上面的第一个版本。
答案 1 :(得分:2)
您选择Vec<Rgb<u8>>
存储格式是因为它更易于处理并且希望它动态增长。但是,正如您注意到的那样,不能保证其存储与Vec<u8>
的兼容性,也不能保证安全的转换。
为什么不以其他方式解决问题并为Vec<u8>
构建方便的外观?
type Rgb = [u8; 3];
#[derive(Debug)]
struct Img(Vec<u8>);
impl Img {
fn new() -> Img {
Img(Vec::new())
}
fn push(&mut self, rgb: &Rgb) {
self.0.push(rgb[0]);
self.0.push(rgb[1]);
self.0.push(rgb[2]);
}
// other convenient methods
}
fn main() {
let mut img = Img::new();
let rgb : Rgb = [1, 2, 3];
img.push(&rgb);
img.push(&rgb);
println!("{:?}", img);
}