为什么在Rust中修改字符串变量时指针的地址没有改变?

时间:2019-11-23 12:10:33

标签: pointers memory rust

我认为在修改字符串时,锈会使堆内存中的另一个数据。因此,我预计当将值推入字符串变量时指针地址会发生变化。

    function getRandIndex(arrayLen) {
      return Math.floor(Math.random() * arrayLen);
    }
    
    function getRandUniqueIndexes(arrayLen, count) {
      const indexes = [];

      while (indexes.length < count) {
        const randIndex = getRandIndex(arrayLen);

        if (!indexes.includes(randIndex)) {
          indexes.push(randIndex);
        }
 
      }

      return indexes;
    }

    // your array with 200 categories
    const categories = Array(200).fill(1).map((_, ndx) => `category-${ndx}`);
    // array with 20 random unique indexes
    const rand20Indexes = getRandUniqueIndexes(categories.length, 20);

    console.log(rand20Indexes.map(catIndex => categories[catIndex]));

但是,结果表明不是。指针的地址未更改,因此我使用矢量类型对其进行了测试。

fn main() {
    let mut hello = String::from("hello");
    println!("{:?}", hello.as_ptr()); // 0x7fcfa7c01be0
    hello.push_str(", world!");
    println!("{:?}", hello.as_ptr()); // 0x7fcfa7c01be0
}

修改向量变量时,其指针地址已更改。字符串和向量类型的内存有什么区别?

2 个答案:

答案 0 :(得分:5)

Vec<T>String可能会保留额外的空间,以避免分配给每个推送操作。这样可以为推送操作提供分摊的O(1)时间。

碰巧的情况是,保证vec!宏创建的矢量没有这样的额外空间,而String::from(&str)没有这样的保证。

有关更多详细信息,请参见https://doc.rust-lang.org/std/vec/struct.Vec.html#capacity-and-reallocation

答案 1 :(得分:2)

String就像Vec<T>¹,因为它同时具有lengthcapacity。如果当前分配的容量足以容纳新字符串,则不需要重新分配基础缓冲区。 The documentation for Vec<T> explains it this way

  

向量的容量是为将添加到向量上的任何将来元素分配的空间量。请勿将其与向量的长度相混淆,后者指定了向量中实际元素的数量。如果向量的长度超过其容量,则其容量将自动增加,但必须重新分配其元素。

     

例如,容量为10且长度为0的向量将是一个空向量,其空间可容纳10个以上元素。 将10个或更少的元素推入向量不会改变其容量,也不会导致重新分配。

但是,即使容量发生变化,仍然不能保证指针值移动。如果有足够的未分配空间,系统分配器本身可以调整分配大小而无需移动分配。这似乎就是您的代码中正在发生的事情。如果您与指针一起打印容量,则可以观察到此行为:

let mut hello = String::from("hello");
for _ in 0..10 {
    println!("({:3}) {:?}", hello.capacity(), hello.as_ptr()); // 0x7fcfa7c01be0
    hello.push_str(", world!");
}
(  5) 0x557624d8da40
( 13) 0x557624d8da40
( 26) 0x557624d8dba0
( 52) 0x557624d8dba0
( 52) 0x557624d8dba0
( 52) 0x557624d8dba0
(104) 0x557624d8dba0
(104) 0x557624d8dba0
(104) 0x557624d8dba0
(104) 0x557624d8dba0

在此示例中,缓冲区被调整了4次大小,但内容仅被移动了一次。


¹实际上,String 新类型的Vec<u8>,这说明了它们为什么一样工作。