为什么Rust编译器在使用Rust 1.31时会破坏借用规则?

Ana*_*kov 5 rust borrow-checker borrowing

我正在研究Rust by Example并从"Alias"页面运行代码:

struct Point {
    x: i32,
    y: i32,
    z: i32,
}

fn main() {
    let mut point = Point { x: 0, y: 0, z: 0 };

    {
        let borrowed_point = &point;
        let another_borrow = &point;

        // Data can be accessed via the references and the original owner
        println!(
            "Point has coordinates: ({}, {}, {})",
            borrowed_point.x, another_borrow.y, point.z
        );

        // Error! Can't borrow point as mutable because it's currently
        // borrowed as immutable.
        let mutable_borrow = &mut point;
        println!(
            "Point has coordinates: ({}, {}, {})",
            mutable_borrow.x, mutable_borrow.y, mutable_borrow.z
        );

        let mutable_borrow2 = &mut point;
        println!(
            "Point has coordinates: ({}, {}, {})",
            mutable_borrow2.x, mutable_borrow2.y, mutable_borrow2.z
        );

        // TODO ^ Try uncommenting this line

        // Immutable references go out of scope
    }

    {
        let mutable_borrow = &mut point;

        // Change data via mutable reference
        mutable_borrow.x = 5;
        mutable_borrow.y = 2;
        mutable_borrow.z = 1;

        // Error! Can't borrow `point` as immutable because it's currently
        // borrowed as mutable.
        //let y = &point.y;
        // TODO ^ Try uncommenting this line

        // Error! Can't print because `println!` takes an immutable reference.
        //println!("Point Z coordinate is {}", point.z);
        // TODO ^ Try uncommenting this line

        // Ok! Mutable references can be passed as immutable to `println!`
        println!(
            "Point has coordinates: ({}, {}, {})",
            mutable_borrow.x, mutable_borrow.y, mutable_borrow.z
        );

        // Mutable reference goes out of scope
    }

    // Immutable references to point are allowed again
    let borrowed_point = &point;
    println!(
        "Point now has coordinates: ({}, {}, {})",
        borrowed_point.x, borrowed_point.y, borrowed_point.z
    );
}
Run Code Online (Sandbox Code Playgroud)

操场

使用Rust编译器()的最新每晚构建在Windows上运行此代码时,我不会遇到编译错误rustc 1.31.0-nightly (f99911a4a 2018-10-23).Rust Playground 中 Rust编译器的最新每晚构建确实提供了预期的编译错误.

为什么是这样?为什么Rust编译器会破坏借用规则?如何在本地修复此问题以获得预期的错误?

She*_*ter 8

当您使用Rust 1.31创建新的Cargo项目时,您将自动选择进入Rust版本2018:

[package]
name = "example"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]
edition = "2018"
Run Code Online (Sandbox Code Playgroud)

这会打开非词汇生命周期,从而实现更智能的借阅检查器形式.如果你想要旧的行为,你可以切换回2015; 这将导致您的代码产生预期的错误.不过,我鼓励您继续使用2018年版.

Rust Playground提供版本之间的切换:

游乐场版开关

Playground目前默认为2015版,在Rust 1.31稳定后,Playground将其默认值更改为2018版.

我如何更改此示例以提供预期的行为

你不能在Rust 2018中.在非词汇生命周期之前,Rust编译器只是不够智能.代码本身是安全的,但编译器无法看到.编译器现在很聪明,所以代码编译.没有理由使用编译器模式来使本质上正确的代码无法编译.

您应该通过示例提交Rust问题,让他们知道他们的示例在Rust 2018中不再有效.


hel*_*low 5

解决方案是代码的前奏.

数据可以不加任意地借用,但是在不可避免地借用的情况下,原始数据不能可靠地借用.另一方面,一次只允许一次可变借入.只有在可变引用超出范围之后,才能再次借用原始数据.

这意味着,您可以随意借用一个值,但一次只能有一个可变借位(在一个范围内).

你可能想知道为什么代码编译#![feature(nll)].

原因是'nll'(非词汇生命周期)允许编译器为超出范围范围(在{和之间的所有内容})的借用创建生命周期.现在可以看出,在使用借来的印刷价值之后,它将不再被使用,因此借款的有效期就在此之后结束println!.

这不会破坏上述任何规则.你不能同时拥有可变借款,例如

let mut point = Point { x: 0, y: 0, z: 0 };

let p1 = &mut point;
let p2 = &point;

println!("Point has coordinates: ({}, {})", p1.x, p2.y);
Run Code Online (Sandbox Code Playgroud)

不行!记在脑子里.