Vector包含数据,但报告长度为0,可以被某些函数访问



我已经在Rust中为相机库编写了一个包装器,该包装器可以命令和操作相机,并使用bindgen将图像保存到文件中。一旦我命令开始曝光(基本上是告诉相机拍摄图像),我就可以使用以下形式的函数获取图像:

pub fn GetQHYCCDSingleFrame(
handle: *mut qhyccd_handle,
w: *mut u32,
...,
imgdata: &mut [u8],) -> u32 //(u32 is a retval)

在c++中,这个函数是:

uint32_t STDCALL GetQHYCCDSingleFrame(qhyccd_handle: *handle, ..., uint8_t *imgdata)

在c++中,我可以传入一个形式为imgdata = new unsigned char[length_buffer]的缓冲区,该函数将用来自相机的图像数据填充缓冲区。

在Rust中,类似地,我可以以Vec:let mut buffer: Vec<u8> = Vec::with_capacity(length_buffer)的形式传入缓冲区。

目前,我构建代码的方式是,有一个主结构体,设置如图像的宽度和高度,相机手柄,和其他,包括图像缓冲区。该结构体已初始化为mut,如:

let mut main_settings = MainSettings {
width: 9600,
...,
buffer: Vec::with_capacity(length_buffer),
}

我写了一个单独的函数,它将主结构体作为参数,并调用GetQHYCCDSingleFrame函数:

fn grab_image(main_settings: &mut MainSettings) {
let retval = unsafe { GetQHYCCDSingleFrame(main_settings.cam_handle, ..., &mut main_settings.image_buffer) };
}

在调用这个函数之后,如果我检查main_settings.image_buffer的长度和容量:

println!("Elements in buffer are {}, capacity of buffer is {}.", main_settings.image_buffer.len(), main_settings.image_buffer.capacity());

的长度为0,而buffer_length为容量。类似地,输出任何索引(如main_settings.image_buffer[0]或1)将导致紧急退出,显示len is 0

这会让我认为GetQHYCCDSingleFrame代码不能正常工作,但是,当我使用fitsiohdu.write_region保存image_buffer到文件时(这里链接的fitsio文档),我使用:

let ranges = [&(x_start..(x_start + roi_width)), &(y_start..(y_start+roi_height))];
hdu.write_region(&mut fits_file, &ranges, &main_settings.image_buffer).expect("Could not write to fits file");

这将一个实际的图像保存到具有合适大小的文件中,并且是一个完美的图像(如果我使用c++程序,它看起来正是这样)。然而,当我试图打印缓冲区时,由于某种原因是空的,然而hdu.write_region代码能够以某种方式访问数据。

目前,我的(不太好的)解决方法是创建另一个向量,从保存的文件中读取数据并保存到缓冲区中,然后缓冲区中有正确数量的元素:

main_settings.new_buffer = hdu.read_region(&mut fits_file, &ranges).expect("Couldn't read fits file");  

为什么我不能访问原始缓冲区,为什么它报告长度0,当hdu.write_region函数可以从某个地方访问数据?它究竟从哪里访问数据,我怎样才能正确地访问它?我对借用和引用有点陌生,所以我相信我可能在借用/引用缓冲区时做错了什么,或者是别的什么?

很抱歉说来话长,但细节可能对这里的一切都很重要。谢谢!

首先,您需要知道Vec<u8>&mut [u8]与C或c++的uint8_t *并不完全相同。主要区别在于Vec<u8>&mut [u8]保存了数组或切片的大小,而uint8_t *没有。与C/c++指针等价的Rust指针是原始指针,如*mut [u8]。构建原始指针是安全的,但需要使用unsafe。然而,即使它们是不同的类型,像&mut [u8]这样的智能指针也可以毫无问题地转换为原始指针。

其次,Vec的容量与它的大小不同。实际上,为了获得良好的性能,Vec会分配比您使用的更多的内存,以避免在每个添加到vector中的新元素上重新分配内存。然而,长度是所用部件的尺寸。在您的示例中,您要求Vec分配一个长度为length_buffer的堆空间,但是您没有告诉它们考虑使用任何已分配的空间,因此初始长度为0。由于c++不知道Vec并且只使用原始指针,因此它无法改变写入Vec内部的长度,它保持为0。这就是恐慌。

要解决这个问题,我看到了多个解决方案:

  • Vec::with_capacity(length_buffer)更改为vec![0; length_buffer],明确要求从一开始length_buffer的长度

  • 使用unsafe代码显式设置Vec的长度,而不触及里面的内容(使用Vec::from_raw_parts)。这个可能比第一个解决方案快,但我不确定。

  • 使用Box<[u8; length_buffer]>,它类似于Vec,但没有重新分配,其长度是容量

  • 如果您的length_buffer在编译时是恒定的,使用[u8; length_buffer]将更有效,因为不需要分配,但它也有缺点,正如您可能知道的

最新更新