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

Vector contains data but reports length is 0, can be accessed by some functions

我用 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 代码无法正常工作,但是,当我使用 fitsio and hdu.write_region (fitsio docs linked here) 将 image_buffer 保存到文件时,我使用:

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 函数可以从某处访问数据?它究竟从哪里访问数据,我如何才能正确访问它?我对借用和引用有点陌生,所以我相信我可能在 borrowing/referencing 缓冲区中做错了什么,或者是其他什么?

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

嗯,首先,你需要知道 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 的堆 space,但您没有告诉他们考虑使用任何已分配的 space ,所以初始长度为 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] 会更有效率,因为不需要分配,但它有缺点,你可能知道