Почему Rust допускает мутацию через поле ссылки с использованием неизменяемой привязки? - PullRequest
0 голосов
/ 02 мая 2018

Если у меня есть неизменяемая переменная, связанная со структурой, Rust обычно не позволяет мне изменять поля структуры или поля собственных дочерних структур.

Однако, если поле является изменяемой ссылкой, Rust будет позволять мне изменять объект, на который ссылаются, несмотря на то, что моя привязка неизменна.

Почему это разрешено? Разве это не противоречит нормальным правилам Руста об неизменности?

Rust не будет позвольте мне сделать то же самое с помощью неизменяемой ссылки, поэтому неизменяемая ссылка имеет поведение, отличное от неизменяемой привязки.

Пример кода:

struct Bar {
    val: i32,
}

struct Foo<'a> {
    val: i32,
    bar: Bar,
    val_ref: &'a mut i32,
}

fn main() {
    let mut x = 5;

    {
        let foo = Foo { 
            val: 6, 
            bar: Bar { val: 15 },
            val_ref: &mut x
        };

        // This is illegal because binding is immutable
        // foo.val = 7;

        // Also illegal to mutate child structures
        // foo.bar.val = 20;

        // This is fine though... Why?
        *foo.val_ref = 10;

        let foo_ref = &foo;

        // Also illegal to mutate through an immutable reference
        //*foo_ref.val_ref = 10;
    }

    println!("{}", x);
}

1 Ответ

0 голосов

Короткий способ объяснить это состоит в том, что изменчивость в ссылках и изменчивость в переменных ортогональны друг другу. Две формы изменчивости связаны в том смысле, что мы можем только позаимствовать что-то из изменяемой переменной (или связывания). Кроме этого, каждая двоичная комбинация возможна в Rust:

               reference mutability
            -----------------------------
variable   |     x: &T  |      x: &mut T |
mutability |------------+----------------|
           | mut x: &T  |  mut x: &mut T |
            -----------------------------

Мы можем вспомнить множество примеров кода, иллюстрирующих, что можно сделать с такой переменной x. Например, неизменяемая переменная изменяемой ссылки может изменять другой элемент, но не саму себя:

let mut a = 5;
let mut b = 3;
let x: &mut i32 = &mut a;

*x = 10; // ok

x = &mut b; // nope! [E0384]
*x = 6;

Даже как поле в структуре, это не противоречит гарантиям безопасности Rust. Если переменная неизменно связана со значением структуры, каждое из полей также будет неизменным. В этом примере:

let mut x = 5;
let foo = Foo { 
    val: 6, 
    bar: Bar { val: 15 },
    val_ref: &mut x
};
*foo.val_ref = 10;

Здесь не было применено никаких мутаций foo: foo.val_ref все еще указывает на x. Первый может быть видоизменен, потому что он заимствован. Ссылки проверяются независимо от заимствования. Параметр времени жизни 'a в Foo позволяет компилятору отслеживать заимствования.

Этот второй пример (показанный ниже) не работает, потому что из &Foo мы можем получить ссылки только на его поля (например, на val_ref: &mut i32). В свою очередь, для предотвращения алиасинга, &&mut i32 может быть приведен только к &i32. Нельзя заимствовать данные изменчиво через неизменную ссылку.

let foo_ref = &foo;
*foo_ref.val_ref = 10; // error[E0389]

Руст не позволит мне сделать то же самое с помощью неизменной ссылки. Таким образом, неизменяемая ссылка имеет другое поведение, чем неизменяемая ссылка.

Точно!

Смотри также:

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