首页 > 解决方案 > 是 Vec 中的数据吗总是密密麻麻?

问题描述

在 rust 中看到这个“快捷方式”代码是一种常见的模式:

unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
    ::std::slice::from_raw_parts(
        (p as *const T) as *const u8,
        ::std::mem::size_of::<T>(),
    )
}

IE。给定一个结构,不安全地将底层指针转换为&[u8]以读取字节。

但是,在使用时采用相同的方法是否有效Vec<T>

例如,这似乎有效:

use std::mem::size_of;
use std::slice::from_raw_parts;

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Point {
    pub x: u8,
    pub y: u8,
    pub z: u8,
}


fn as_bytes(data: &[Point]) -> &[u8] {
    unsafe { 
        let raw_pointer = data.as_ptr();
        from_raw_parts(raw_pointer as *const u8, size_of::<Point>() * data.len())
    }
}

fn main() {
    let points = vec![Point{x: 0u8, y: 1u8, z: 2u8}, Point{x: 3u8, y: 4u8, z: 5u8}];
    let slice = points.as_slice();
    println!("{:?}", slice);

    let bytes = as_bytes(slice);
    println!("{:?}", bytes);
    
    assert!(bytes.len() == 6);
    assert!(bytes[0] == 0u8);
    assert!(bytes[1] == 1u8);
    assert!(bytes[2] == 2u8);
    assert!(bytes[3] == 3u8);
    assert!(bytes[4] == 4u8);
    assert!(bytes[5] == 5u8);
}

...但是假设以这种Vec<T>方式表示为单个连续的数据块是否可靠?

https://doc.rust-lang.org/std/vec/struct.Vec.html#capacity-and-reallocation上的文档说:

如果 Vec 已分配内存,则它指向的内存位于堆上(由分配器定义,Rust 配置为默认使用),它的指针按顺序指向 len 初始化的连续元素(如果您将其强制为切片),然后是 capacity-len 逻辑上未初始化的连续元素。

...但我不确定我是否理解它的含义。这实际上是否意味着Vec<T>底层指针指向一个长度size_of::<T>* Vec 长度的内存块?

标签: rust

解决方案


是的,Vec<T>可以将 a 制成可以被视为指向长度为的内存块的指针std::mem::size_of::<T>() times the length of Vec

有一个警告,因为您真正感兴趣的是sliceof T,它Vec可以提供;Vec本身应该被认为是一个实现细节。除此之外:

  1. AVec<T> 可以 deref到 slice [T]。拿那个slice

  2. Rust 参考定义a与他们切片的slice部分具有相同的布局。Array所以当我们从 aVec<T>到 a[T]时,这个长度切片n保证与数组具有相同的内存布局[T; n]

  3. Rust 参考定义了 an的内存布局Array

    数组的布局使得数组的第 n 个元素从数组的开头偏移n * the size of the type字节。数组的[T; n]大小为size_of::<T>() * n且对齐方式相同T

  4. 我们知道n(来自[T])并且我们知道“the size of the type字节”(通过mem::size_of<T>())。由于数组的所有成员必须始终完全初始化,并且鉴于上一段中的两个句子,我们知道访问所有字节是安全的,直到mem::size_of<T>() * length of Vec(实际上长度为slice,它引入了数组内存布局规则)。

为了利用这一切,你应该确保你得到了sliceVec一个,as_ptr()在切片上使用,并转换你得到的原始指针。这确保了上述定义的顺序。你fn as_bytes(data: &[Point]) -> &[u8]是完全正确的。


推荐阅读