Rust:如何将某些堆内存的所有权转移出函数?

时间:2014-11-25 18:34:16

标签: memory-management struct rust object-lifetime

我正在尝试编写一个函数,将命令行实用程序(image-magick)的标准输出加载到结构的成员中。我认为因为图像可以是MB,所以我也可以尽可能地避免复制。

/// An image data container used internally.
/// Images are 8-bit single channel for now.
pub struct Image<'a> {
    /// Width of the image in pixels.
    pub width: u32,
    /// Height of the image in pixels.
    pub height: u32,
    /// The buffer containing the image data, as a slice.
    pub pixels: &'a [u8],
}

// Load a file by first using imageMagick to convert it to a .pgm file.
fn load_using_magick<'a>(path: Path) -> Image<'a> {
    use std::io::Command;

    let output:IoResult<ProcessOutput> = Command::new("convert")
        .arg("-format pgm")
        .arg("-depth 8")
        .arg(path)
        .arg("-")
        .output();
    let output_unwrapped:ProcessOutput = match output {
        Ok(o) => o,
        Err(e) => panic!("Unable to run ImageMagick's convert tool in a separate process! convert returned: {}", e),
    };

    let bytes: &[u8] = match output_unwrapped.status.success() {
        false => panic!("signal or wrong error code from sub-process?"),
        true => output_unwrapped.output.as_slice(),
    };
    // Note, width and height will eventually get parsed out of "bytes"
    // and the returned Imaeg.pixels will point to a slice of the (already)
    // heap allocated memory.  I just need to figure out ownership first...
    Image{width:10,height:10,pixels:bytes}

}

当调用标准库std :: io :: Process :: Command :: output()时,我想要保留的大块堆由标准库(或者可能是内核?)分配。

使用借阅检查器进行编译失败:

src/loaders.rs:41:17: 41:40 error: `output_unwrapped.output` does not live long enough
src/loaders.rs:41         true => output_unwrapped.output.as_slice(),
                                  ^~~~~~~~~~~~~~~~~~~~~~~
src/loaders.rs:21:51: 48:2 note: reference must be valid for the lifetime 'a as defined on the block at 21:50...
src/loaders.rs:21 fn load_using_magick<'a>(path: Path) -> Image<'a> {
src/loaders.rs:22     use std::io::Command;
src/loaders.rs:23 
src/loaders.rs:24     let output:IoResult<ProcessOutput> = Command::new("convert")
src/loaders.rs:25         .arg("-format pgm")
src/loaders.rs:26         .arg("-depth 8")
                  ...
src/loaders.rs:21:51: 48:2 note: ...but borrowed value is only valid for the block at 21:50
src/loaders.rs:21 fn load_using_magick<'a>(path: Path) -> Image<'a> {
src/loaders.rs:22     use std::io::Command;
src/loaders.rs:23 
src/loaders.rs:24     let output:IoResult<ProcessOutput> = Command::new("convert")
src/loaders.rs:25         .arg("-format pgm")
src/loaders.rs:26         .arg("-depth 8")

这对我有意义;无论拥有大量数据我试图保持不变都超出范围,在结构中留下一个悬空指针,我按值返回。那么在我返回之前,如何实际将内存区域的所有权转移到我的struct?

我已经阅读了Rust Lifetimes Manual

1 个答案:

答案 0 :(得分:4)

您的签名声称,无论来电者的生命周期如何,您都可以返回Image<'that_lifetime>。实际上,您声称返回Image<'static>,其中包含&'static [u8],即指向在程序执行的整个持续时间内存在的字节切片的指针。显然,实际采用字节切片的ProcessOutput并没有那么久。

内存区域的所有权属于output_unwrapped,更具体地说属于output_unwrapped.output(a Vec<u8>)。你需要让这两个中的一个活着。最简单的选择是将所有权授予Image:将pixels设为Vec<u8>,然后移出output_unwrapped。当然,这会对所有代码处理Image产生持久影响,因此这是设计问题。是否使图像拥有 sense 拥有内容?缺乏借用是否会导致任何预期用例出现问题? (如果你不能回答这些问题,你可以随时继续尝试,尽管你可能会在几周后才发现。)

一种替代方案是抽象所有权,即允许向量和切片。对于字符串而言,甚至通过std::str::MaybeOwned类型在标准库中支持。我不知道Vec / slice的替代品,但它很容易实现。你只需要决定是否值得额外的麻烦 - 特别是因为pixels由于某种原因(可能不是一个好的?),公开。