Почему компилятор Rust может нарушать правила заимствования при использовании Rust 1.31? - PullRequest
0 голосов
/ 29 октября 2018

Я изучаю Rust на примере и запускаю код из страницы "Псевдоним" :

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
    );
}

Детская площадка

Я не получаю ошибки компиляции при запуске этого кода в Windows с последней ночной сборкой компилятора Rust (rustc 1.31.0-nightly (f99911a4a 2018-10-23)). Последняя ночная сборка компилятора Rust на Rust Playground обеспечивает ожидаемые ошибки компиляции.

Почему это? Почему компилятор Rust может нарушать правила заимствования? Как я могу исправить это локально, чтобы получить ожидаемые ошибки?

Ответы [ 3 ]

0 голосов
/ 29 октября 2018

Решение в прелюдии кода.

Данные могут быть заимствованы неизменно любое количество раз, но, несмотря на то, что данные заимствованы неизменно, исходные данные не могут быть заимствованы. С другой стороны, одновременно допускается только одно изменяемое заимствование. Исходные данные могут быть заимствованы снова только после того, как изменяемая ссылка выйдет за рамки.

Это означает, что вы можете заимствовать значение столько раз, сколько захотите, но одновременно можете иметь только один изменяемый заем (в области действия).

Вам может быть интересно, почему код компилируется с #![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);

не будет работать! Имейте это в виду.

0 голосов
/ 29 октября 2018

Когда вы создаете новый проект Cargo с Rust 1.31, вы автоматически включаете Rust edition 2018:

[package]
name = "example"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]
edition = "2018"

Включает нелексические времена жизни , что обеспечивает более разумную форму проверки заимствования. Если вам нужно старое поведение, вы можете переключиться обратно на 2015; это приведет к тому, что ваш код выдаст ожидаемые ошибки. Однако я призываю вас продолжать использовать издание 2018 года.

Rust Playground предлагает переключение между редакциями:

playground edition switch

Игровая площадка в настоящее время по умолчанию выпускается в 2015 году, и после стабилизации Rust 1.31 игровая площадка изменит свое значение по умолчанию на издание 2018.

Как я могу изменить этот пример, чтобы обеспечить ожидаемое поведение

Вы не можете в Rust 2018. До нелексических времен жизни компилятор Rust был просто недостаточно интеллектуален. Сам код безопасен , но компилятор не мог этого увидеть. Компилятор теперь такой умный, поэтому код компилируется. Нет никаких причин иметь режим компилятора, чтобы сделать некорректный по своей природе код не скомпилированным.

Вам следует сообщить о проблеме в Rust by Example , сообщив им, что их пример больше не действует в Rust 2018.

0 голосов
/ 29 октября 2018

Это так называемые нелексические времена жизни - функция, доступная в настоящее время на языке и недоступная в версии 2015 года. Короче говоря, с ними ссылка отбрасывается, когда она больше не используется, и не в конце области, как раньше (и как указано в документах). Вы можете проверить это с помощью borrowed_point после mutable_borrow - это должно вызвать ошибку даже при NLL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...