为什么在调用Vec :: set_len之前调用Vec :: resize导致Vec有数据?

时间:2017-03-27 23:12:55

标签: arrays vector rust slice

我有一个我不明白的问题:

fn cipher_with(key: &[u8], data: &[u8]) -> Vec<u8> {
    let data_len = 16;

    let mut data = data.to_vec();
    data.resize(data_len, 2);

    let mut output = Vec::<u8>::with_capacity(data_len);
    unsafe { output.set_len(data_len) }

    output
}

fn main() {
    let key = "blabla".as_bytes();
    let data = "lorem ipsum.".as_bytes();
    println!("{:?}", cipher_with(&key, &data));
}

打印:

  

[108,111,114,101,109,32,105,112,115,117,109,46,0,0,0,0]

但它是如何完成的?我从未将此值赋予output

2 个答案:

答案 0 :(得分:5)

要向Peter's answer添加一些详细信息,请查看此注释版本:

fn cipher_with(key: &[u8], data: &[u8]) -> Vec<u8> {
    let data_len = 16;

    let mut data = data.to_vec();
    println!("{:?}", data.as_ptr());
    data.resize(data_len, 2);
    println!("{:?}", data.as_ptr());

    let mut output = Vec::<u8>::with_capacity(data_len);
    println!("{:?}", output.as_ptr());
    unsafe { output.set_len(data_len) }

    output
}
0x7fa6dba27000
0x7fa6dba1e0c0
0x7fa6dba27000

创建第一个向量时,它的长度为12.当它被调整为16时,将进行新的分配并复制数据。这可能是由于分配器的实现,分配器通常将分配分块到桶中。 16将是一个合理的桶大小。

当创建第二个向量时,分配器会移回第一个向量刚刚放弃的相同指针。由于没有其他内容同时改变了这个内存,它仍然包含data中的任何数据。

答案 1 :(得分:3)

您正在使用不安全的Rust,这会给您带来不可预测的结果。

在这种特殊情况下,您将Vec的大小扩展为未初始化的内存。这些值已经发生在那里。

让我们看看一些代码:

let mut data = data.to_vec();

这会复制数据“lorem ipsum”。以向量的形式进入堆。

data.resize(data_len, 2); // data_len = 16

这会将Vec的容量从12个增加到16个,在这种情况下恰好是字节。但实际上,根据我们所看到的情况,看起来实现(或可能是优化器)决定放弃第一个分配的内存范围并将数据复制到新内存更好。

let mut output = Vec::<u8>::with_capacity(data_len);
unsafe { output.set_len(data_len) }

这会创建一个新的矢量,不安全地会给它一个长度。但是你没有初始化它,所以数据将是以前的数据。

看起来data.resize()实际上复制了值而不是仅仅删除了向量的末尾。分配output时,它被分配了之前使用的相同内存块,这就是它包含"lorem ipsum."的原因。