有没有办法强制Rust让我使用可能移动的值?

Eva*_*van 3 rust

我是否忘记了借用和移动是如何工作的?

let mut v = vec![1, 2, 3]; // I have some uncopyable value

if false {
    let t = v; // I might do something that consumes it
}

println!("{:?}", v); // in some condition, I know for sure that I didn't consume it
Run Code Online (Sandbox Code Playgroud)

我可以以某种方式使用一个unsafe子句来告诉编译器相信我吗?

任何解决方案都必须没有运行时开销

Cod*_*aos 11

即使在不安全的代码中,编译器也不允许您访问可能已移出值的变量.

一些解决方法:

  • 包裹它Option.然后,您可以使用该take方法移出数据,留下一个None值.

    这是我推荐用于局部变量的方法.

  • 用空向量替换原始向量.这很便宜,因为空矢量不分配.

    let t = std::mem::replace(&mut v, Vec::new());
    
    Run Code Online (Sandbox Code Playgroud)

    这是与C++移动最接近的等价物,其描述如下:

    除非另有规定,否则此类移动物体应置于有效但未指定的状态.

  • 包裹它ManuallyDrop(这比mem::forget因为恐慌发生时不会降低值更安全).将它手动放在它仍然初始化的路径的末尾.使用deref在仍然有效时访问它.ptr::read复制该值,将原始位置视为无效/未初始化.

    这不应该有任何运行时开销,但我强烈建议不要在局部变量上使用它.这不值得复杂和风险.

    use std::mem::ManuallyDrop;
    use std::ptr;
    
    fn main() {
        let flag = //...;
        unsafe {
            let mut v = ManuallyDrop::new(vec![1, 2, 3]); // I have some uncopyable value
    
            if flag {
                let t = ptr::read(&*v); // I might do something that consumes it
                // don't touch *v from now on
                println!("{:?}", t);
            }
    
            if !flag {
                println!("{:?}", *v); // in some condition, I know for sure that I didn't consume it
                ManuallyDrop::drop(&mut v);
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    操场


She*_*ter 5

不。

我确定我没有消费它

仅仅因为您没有编写任何不消耗它的代码并不意味着它没有被消耗。所有权和条件执行代码进一步讨论了基于类型和堆栈的丢弃标志的机制,但从概念上讲,您的代码是:

let v = vec![1, 2, 3];

if false {
    let _t = v;
    drop(_t);
} else {
    drop(v);
}

println!("{:?}", v);
Run Code Online (Sandbox Code Playgroud)

一旦条件结束,你的价值就消失了。(在实现方面,删除确实发生在函数的末尾,但语义没有表达出来)。

在某种情况下

该条件将是else您的if语句块:

if false {
    let _t = v;
} else {
    println!("{:?}", v);
}
Run Code Online (Sandbox Code Playgroud)