【问题标题】:Vec<MyTrait> without N heap allocations?Vec<MyTrait> 没有 N 堆分配?
【发布时间】:2016-05-17 04:34:39
【问题描述】:

我正在尝试将一些 C++ 代码移植到 Rust。它由几种切片(字符串引用、惰性求值字符串引用、物理文件的一部分)组成一个虚拟 (.mp4) 文件,并根据结果提供 HTTP 请求。 (如果你好奇,请参阅Mp4File,它利用了FileSlice 接口及其在http.h 中的具体实现。)

问题是:我希望需要尽可能少的堆分配。假设我有一些resource::Slice 的实现,希望我可以自己解决。然后我想做一个组成它们的那个:

pub trait Slice : Send + Sync {
    /// Returns the length of the slice in bytes.
    fn len(&self) -> u64;

    /// Writes bytes indicated by `range` to `out.`
    fn write_to(&self, range: &ByteRange,
                out: &mut io::Write) -> io::Result<()>;
}

// (used below)
struct SliceInfo<'a> {
    range: ByteRange,
    slice: &'a Slice,
}

/// A `Slice` composed of other `Slice`s.    
pub struct Slices<'a> {
    len: u64,
    slices: Vec<SliceInfo<'a>>,
}

impl<'a> Slices<'a> {
    pub fn new() -> Slices<'a> { ... }
    pub fn append(&mut self, slice: &'a resource::Slice) { ... }
}

impl<'a> Slice for Slices<'a> { ... }

并使用它们以尽可能少的堆分配来附加大量切片。简化,像这样:

struct ThingUsedWithinMp4Resource {
    slice_a: resource::LazySlice,
    slice_b: resource::LazySlice,
    slice_c: resource::LazySlice,
    slice_d: resource::FileSlice,
}

struct Mp4Resource {
    slice_a: resource::StringSlice,
    slice_b: resource::LazySlice,
    slice_c: resource::StringSlice,
    slice_d: resource::LazySlice,
    things: Vec<ThingUsedWithinMp4Resource>,
    slices: resource::Slices
}

impl Mp4Resource {
    fn new() {
        let mut f = Mp4Resource{slice_a: ...,
                                slice_b: ...,
                                slice_c: ...,
                                slices: resource::Slices::new()};
        // ...fill `things` with hundreds of things...
        slices.append(&f.slice_a);
        for thing in f.things { slices.append(&thing.slice_a); }
        slices.append(&f.slice_b);
        for thing in f.things { slices.append(&thing.slice_b); }
        slices.append(&f.slice_c);
        for thing in f.things { slices.append(&thing.slice_c); }
        slices.append(&f.slice_d);
        for thing in f.things { slices.append(&thing.slice_d); }
        f;
    }
}

但这不起作用。附加行会导致错误“f.slice_* 寿命不够长”、“引用必须在...的块上定义的生命周期 'a 内有效”、“...但是借用的值仅对语句块后缀"。我认为这类似于this question 关于自引用结构。基本上就是这样,有更多的间接性。显然这是不可能的。

那我能做些什么呢?

我想我很乐意将所有权授予append 中的resource::Slices,但我不能在Vec&lt;SliceInfo&gt; 中使用的SliceInfo 中添加resource::Slice,因为resource::Slice 是一个特征,并且特征没有大小。我可以改为使用Box&lt;resource::Slice&gt;,但这意味着为每个切片分配单独的堆。我想避免这种情况。 (每个 Mp4Resource 可以有数千个切片。)

我正在考虑做一个枚举,比如:

enum BasicSlice {
    String(StringSlice),
    Lazy(LazySlice),
    File(FileSlice)
};

并在SliceInfo 中使用它。我想我可以完成这项工作。但它肯定会限制我的resource::Slices 类的效用。我想让它在我没有预料到的情况下轻松使用,最好不必每次都定义一个新的枚举。

还有其他选择吗?

【问题讨论】:

  • 如果结构体中包含有限生命周期项,则该结构体也具有有限生命周期。
  • @WiSaGaN 关于在结构中包含有限生命周期的项目有什么说法?我的第一种方法是包含一个与结构一样长的项目。但我认为没有办法表达那个生命周期,因此自引用结构是不可能的。无论如何,正如我在问题中所说的那样,我很高兴找到另一种方式......
  • 好吧,但事实并非如此。至少在 C++ 术语中,一个类的成员是按顺序构造的,然后以相反的顺序被破坏,我有一个后来的成员,它引用了一个早期的成员。这是一件安全的事情,如果有帮助的话,我很乐意在两个单独的堆分配中完成它们。但似乎不管怎样,这辈子还是要过去的……

标签: rust lifetime


【解决方案1】:

您可以将User 变体添加到您的BasicSlice 枚举中,该枚举采用Box&lt;SliceInfo&gt;。这样只有用户的特殊情况才会进行额外分配,而正常路径会得到优化。

【讨论】:

    猜你喜欢
    • 2017-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-07
    • 2022-08-07
    • 2018-10-19
    • 2019-10-25
    相关资源
    最近更新 更多