如何在没有分配的情况下从 Vec 获取范围并在 Rust 中生成新的 Vec?
How to take range from Vec without allocations and produce new Vec in Rust?
我想从 Vec
中提取一个范围,方法是使用它并返回一个仅包含所需数据的新 Vec。我希望无需复制数据或进行任何分配即可实现这一点。在 C 中,我会根据需要移动指针、更改长度字段和释放内存。
目前我正在使用 drain
,但我不确定它的性能特征(它会复制数据吗?)或它的惯用程度。例如,如果我想要前 4 个元素,我会这样做:
fn main() {
let mut a = vec![1,2,3,4,5,6,7,8];
// Only want to keep the first 4 elements
let a: Vec<u32> = a.drain(..4).collect();
println!("{:?}", a);
}
我看过 from_raw_parts
方法,但如果可能的话,我想避免使用指针之类的东西。
一般解决方案需要复制数据
一般来说,一个Vec
由
组成
- 一个整数
capacity
- 可以包含
capacity
元素的内存区域(我将其称为 缓冲区 )和
- 一个整数
size
,size <= capacity
。
capacity
是缓冲区 在不调整大小的情况下可以 包含的元素数,size
是缓冲区 可以包含的元素数 当前包含。
保存在 Vec
中的元素位于缓冲区的开头(因此,从索引 0
到 size - 1
)。这意味着要创建一个包含旧 Vec
子范围的新 Vec
,在一般情况下, 需要复制一些元素 – 如果子范围不从第一个元素开始,新 Vec
中的第一个元素将与旧 Vec
中的第一个元素不同,你不能不复制就重用缓冲区。
在 drain()
的情况下,这发生在 the Drop implementation of Drain
:
if self.0.tail_len > 0 {
unsafe {
let source_vec = self.0.vec.as_mut();
// memmove back untouched tail, update to new length
let start = source_vec.len();
let tail = self.0.tail_start;
if tail != start {
let src = source_vec.as_ptr().add(tail);
let dst = source_vec.as_mut_ptr().add(start);
ptr::copy(src, dst, self.0.tail_len);
}
source_vec.set_len(start + self.0.tail_len);
}
}
如果要保留前面的元素
如果你只想保留前面的元素而删除后面的元素,有一个不复制元素的解决方案。您可以使用 truncate()
. You can look at its source,它不会复制数据(在我看来,在这种情况下,它比使用 drain()
更符合习惯)。
fn main() {
let mut a = vec![1, 2, 3, 4, 5, 6, 7, 8];
a.truncate(4);
assert_eq!(a, vec![1, 2, 3, 4]);
}
为了完整起见:这不会缩小向量的容量。如果您截断了很多元素,然后保留截断后的 Vec
而不添加新元素,那么您就是在浪费内存。在那种情况下,截断后调用 shrink_to_fit()
可能是个好主意(尽管此 可能 复制 Vec
内容,因此更昂贵)。
drain()
也不应该在这种特殊情况下复制数据(self.0.tail_len == 0
在这种情况下,所以上面的代码片段不会被执行),但与直接使用 truncate
。
我想从 Vec
中提取一个范围,方法是使用它并返回一个仅包含所需数据的新 Vec。我希望无需复制数据或进行任何分配即可实现这一点。在 C 中,我会根据需要移动指针、更改长度字段和释放内存。
目前我正在使用 drain
,但我不确定它的性能特征(它会复制数据吗?)或它的惯用程度。例如,如果我想要前 4 个元素,我会这样做:
fn main() {
let mut a = vec![1,2,3,4,5,6,7,8];
// Only want to keep the first 4 elements
let a: Vec<u32> = a.drain(..4).collect();
println!("{:?}", a);
}
我看过 from_raw_parts
方法,但如果可能的话,我想避免使用指针之类的东西。
一般解决方案需要复制数据
一般来说,一个Vec
由
- 一个整数
capacity
- 可以包含
capacity
元素的内存区域(我将其称为 缓冲区 )和 - 一个整数
size
,size <= capacity
。
capacity
是缓冲区 在不调整大小的情况下可以 包含的元素数,size
是缓冲区 可以包含的元素数 当前包含。
保存在 Vec
中的元素位于缓冲区的开头(因此,从索引 0
到 size - 1
)。这意味着要创建一个包含旧 Vec
子范围的新 Vec
,在一般情况下, 需要复制一些元素 – 如果子范围不从第一个元素开始,新 Vec
中的第一个元素将与旧 Vec
中的第一个元素不同,你不能不复制就重用缓冲区。
在 drain()
的情况下,这发生在 the Drop implementation of Drain
:
if self.0.tail_len > 0 {
unsafe {
let source_vec = self.0.vec.as_mut();
// memmove back untouched tail, update to new length
let start = source_vec.len();
let tail = self.0.tail_start;
if tail != start {
let src = source_vec.as_ptr().add(tail);
let dst = source_vec.as_mut_ptr().add(start);
ptr::copy(src, dst, self.0.tail_len);
}
source_vec.set_len(start + self.0.tail_len);
}
}
如果要保留前面的元素
如果你只想保留前面的元素而删除后面的元素,有一个不复制元素的解决方案。您可以使用 truncate()
. You can look at its source,它不会复制数据(在我看来,在这种情况下,它比使用 drain()
更符合习惯)。
fn main() {
let mut a = vec![1, 2, 3, 4, 5, 6, 7, 8];
a.truncate(4);
assert_eq!(a, vec![1, 2, 3, 4]);
}
为了完整起见:这不会缩小向量的容量。如果您截断了很多元素,然后保留截断后的 Vec
而不添加新元素,那么您就是在浪费内存。在那种情况下,截断后调用 shrink_to_fit()
可能是个好主意(尽管此 可能 复制 Vec
内容,因此更昂贵)。
drain()
也不应该在这种特殊情况下复制数据(self.0.tail_len == 0
在这种情况下,所以上面的代码片段不会被执行),但与直接使用 truncate
。