当Vec正在移动时,我能否(不安全地)持有对Vec元素的指针?

5

我正在实现一个算法,为了保持所需的时间复杂度,我希望在 Vec 移动时能够持有指向其中一个元素的指针。

具体来说,就是这样:

fn main() {
    let mut v: Vec<usize> = vec![1, 2, 3];
    let ptr: *mut usize = &mut v[1] as *mut usize;
    let mut u: Vec<usize> = v;
    assert!(ptr == &mut u[1] as *mut usize);
    println!("{}", unsafe { *ptr });
}

实际代码更加复杂,它涉及一种类似树的数据结构,每个顶点都拥有一个子Vec。这里我不是询问编程风格,而是在问这段代码是否可以依赖于我认为它应该做的事情。
由于Vec必须在堆上保存其内容,并且移动等同于Rust中的memcpy,我的想法是Vec可移动的事实意味着我的代码是正确的(即没有未定义的行为)。这个想法正确吗?

除非移动的向量超出其容量(通过添加元素进行调整),否则您将得到一个悬空指针(因为所有数据都被复制到具有更大容量的另一个位置)。 - Alexey Larionov
Vec 是否确实保证在任何情况下都将其元素保存在堆上?它是否有可能进行一些小的向量优化(例如,如果只有最多4个元素,则将元素保存在数组中)? - phimuemue
@AlexLarionov 我可以保证移动的向量不会超出其容量被调整大小。 - Bernard
@phimuemue 嗯,Vecfrom_raw_partsinto_raw_parts 方法,如果存在小向量优化,我认为这些方法就无法实现。 - Bernard
2
@phimuemue _Vec永远不会执行“小优化”,其中元素实际上存储在堆栈上_(参考 - Ömer Erden
1个回答

3

你可以像这样做:

fn main() {
    let mut v: Vec<usize> = vec![1, 2, 3];
    let ptr: *mut usize = &mut v[1] as *mut usize;
    let mut u: Vec<usize> = v;
    assert!(ptr == &mut u[1] as *mut usize);
    // I copied this from Stack Overflow without reading the surrounding prose 
    println!("{}", unsafe { *ptr });
}

您应该记录unsafe块的安全条件以及您如何履行它们。在此情况下...

  1. Vec的元素是堆分配的:

    如果一个Vec已经分配了内存,那么它指向的内存就在堆上

  2. 在移动期间,您没有更改后备分配。这可能是由于修改Vec导致调整大小或销毁。

  3. 当您尝试使用引用时,不存在指向相同元素的别名引用(包括任何可以到达相同元素的内容)。


对于这个例子,我只会使用一个索引(1)。对于您的树案例,我会尝试使用索引的向量。请进行基准测试,以查看是否有明显差异。


在不安全的代码块中,Vec 本身和 &mut usize 将共存,编译器无法知道 &mut usize 是来自于 Vec。通常情况下,是否可以让对 u 的可变引用与对 u[1] 的可变引用共存(只要我没有第二个对 u[1] 的引用)? - Bernard
u内部没有任何引用,只有更多的原始指针。拥有对u的可变引用会让人怀疑(因为通常是不允许的)。你将能够获得第二个可变引用到该元素。 - Shepmaster
抱歉,我想我表达不够清楚。我的意思是像这样的东西。是的,在安全的 Rust 中不可能做到这样的事情。但是如果我想通过一个期望引用的 API 来改变指针中的元素(如链接代码所示),由于可变引用和 u 的共存,这是否会立即成为 UB?我可以通过 &mut u[1] 获取另一个可变引用,我认为那将是立即 UB;但是如果我没有这样做,代码是否仍然是良好定义的? - Bernard
1
正如我所提到的,u内部没有任何引用。由于它没有引用,所以不可能与另一个引用存在别名。 - Shepmaster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接