转换 Vec u32 ;到 Vec u8 ;就地和最小的开销 [英] Converting a Vec<u32> to Vec<u8> in-place and with minimal overhead
问题描述
我正在尝试将 u32
的 Vec
转换为 u8
的 Vec
,最好是就地,没有太多的开销.
I'm trying to convert a Vec
of u32
s to a Vec
of u8
s, preferably in-place and without too much overhead.
我当前的解决方案依赖于不安全的代码来重新构建Vec
.有没有更好的方法来做到这一点,我的解决方案有哪些风险?
My current solution relies on unsafe code to re-construct the Vec
. Is there a better way to do this, and what are the risks associated with my solution?
use std::mem;
use std::vec::Vec;
fn main() {
let mut vec32 = vec![1u32, 2];
let vec8;
unsafe {
let length = vec32.len() * 4; // size of u8 = 4 * size of u32
let capacity = vec32.capacity() * 4; // ^
let mutptr = vec32.as_mut_ptr() as *mut u8;
mem::forget(vec32); // don't run the destructor for vec32
// construct new vec
vec8 = Vec::from_raw_parts(mutptr, length, capacity);
}
println!("{:?}", vec8)
}
推荐答案
每当编写
不安全
块时,我强烈鼓励人们在块上添加注释,解释为什么您认为代码实际上是安全的.这类信息对以后阅读代码的人很有用.
Whenever writing an
unsafe
block, I strongly encourage people to include a comment on the block explaining why you think the code is actually safe. That type of information is useful for the people who read the code in the future.
不要添加关于幻数"4的评论,只需使用mem::size_of::
.我什至会为 u8
使用 size_of
并执行除法以获得最大清晰度.
Instead of adding comments about the "magic number" 4, just use mem::size_of::<u32>
. I'd even go so far as to use size_of
for u8
and perform the division for maximum clarity.
您可以从 unsafe
块中返回新创建的 Vec.
You can return the newly-created Vec from the unsafe
block.
正如评论中提到的,像这样转储"一个数据块会使数据格式依赖于平台;你会在小端和大端系统上得到不同的答案.这可能会导致将来出现大量调试问题.文件格式要么将平台字节序编码到文件中(使读者的工作更难),要么只将特定的字节序写入文件(使作者的工作更难).
As mentioned in the comments, "dumping" a block of data like this makes the data format platform dependent; you will get different answers on little endian and big endian systems. This can lead to massive debugging headaches in the future. File formats either encode the platform endianness into the file (making the reader's job harder) or only write a specific endinanness to the file (making the writer's job harder).
我可能会将整个 unsafe
块移动到一个函数中并为其命名,仅用于组织目的.
I'd probably move the whole unsafe
block to a function and give it a name, just for organization purposes.
你不需要导入Vec
,它在前奏中.
You don't need to import Vec
, it's in the prelude.
use std::mem;
fn main() {
let mut vec32 = vec![1u32, 2];
// I copy-pasted this code from StackOverflow without reading the answer
// surrounding it that told me to write a comment explaining why this code
// is actually safe for my own use case.
let vec8 = unsafe {
let ratio = mem::size_of::<u32>() / mem::size_of::<u8>();
let length = vec32.len() * ratio;
let capacity = vec32.capacity() * ratio;
let ptr = vec32.as_mut_ptr() as *mut u8;
// Don't run the destructor for vec32
mem::forget(vec32);
// Construct new Vec
Vec::from_raw_parts(ptr, length, capacity)
};
println!("{:?}", vec8)
}
我对这段代码最大的未知担忧在于与 Vec
关联的内存的对齐.
My biggest unknown worry about this code lies in the alignment of the memory associated with the Vec
.
Rust 的底层分配器 分配 和 释放内存与一个特定的Layout
Layout
包含指针的大小和对齐等信息.
Rust's underlying allocator allocates and deallocates memory with a specific Layout
. Layout
contains such information as the size and alignment of the pointer.
我假设这段代码需要 Layout
来匹配对 alloc
和 dealloc
的成对调用.如果是这种情况,删除Vec
由 Vec
构造而成,可能会告诉分配器错误的对齐方式,因为该信息是 基于元素类型.
I'd assume that this code needs the Layout
to match between paired calls to alloc
and dealloc
. If that's the case, dropping the Vec<u8>
constructed from a Vec<u32>
might tell the allocator the wrong alignment since that information is based on the element type.
如果没有更好的知识,最好"的做法是保持 Vec
原样,只需获取一个 &[u8]
它.切片与分配器没有交互,避免了这个问题.
Without better knowledge, the "best" thing to do would be to leave the Vec<u32>
as-is and simply get a &[u8]
to it. The slice has no interaction with the allocator, avoiding this problem.
即使不与分配器交互,您也需要注意对齐!
Even without interacting with the allocator, you need to be careful about alignment!
另见:
这篇关于转换 Vec u32 ;到 Vec u8 ;就地和最小的开销的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!