В Rust, в чем разница между «слежкой» и «изменчивостью»? - PullRequest
0 голосов
/ 10 ноября 2018

В главе 3 книги Rust , Переменные и изменчивость , мы пройдем несколько итераций по этой теме, чтобы продемонстрировать неизменное поведение переменных по умолчанию в Rust:

fn main() {
    let x = 5;
    println!("The value of x is {}", x);
    x = 6;
    println!("The value of x is {}", x);
}

Какие выходы:

error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:4:5
  |
2 |     let x = 5;
  |         -
  |         |
  |         first assignment to `x`
  |         help: make this binding mutable: `mut x`
3 |     println!("The value of x is {}", x);
4 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable

Однако из-за того, что Rust принимает shadowing переменные, мы можем просто сделать это, чтобы изменить значение тем не менее "неизменного" x:

fn main() {
    let x = 5;
    println!("The value of x is {}", x);
    let x = 6;
    println!("The value of x is {}", x);
}

Какие выходы (пропуская детали):

The value of x is 5
The value of x is 6

Как ни странно, этот код также выводит вышеуказанную пару строк в качестве вывода, несмотря на то, что мы не вызываем let, но вместо этого mut в первый раз x связан с 5:

fn main() {
    let mut x = 5;
    println!("The value of x is {}", x);
    x = 6;
    println!("The value of x is {}", x);
}

Эта неоднозначность того, как переменные (не реально) защищены от переназначения, кажется, противоречит заявленной цели защиты значений, связанных с неизменяемыми - по умолчанию переменными Rust. Из той же главы (которая также содержит раздел Shadowing ):

Важно, чтобы мы получали ошибки во время компиляции при попытке изменить значение, которое мы ранее определили как неизменный, потому что это Сама ситуация может привести к ошибкам. Если одна часть нашего кода работает на предположение, что значение никогда не изменится, и другая часть нашего код изменяет это значение, возможно, что первая часть кода не будет делать то, что было задумано. Причина такого рода ошибки может быть трудно выследить после факта, особенно когда второй кусок кода меняет значение только иногда.

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

Если я могу заставить эту важную особенность моего неизменного x обойти стороной достаточно невинный вызов let, зачем мне нужен mut? Есть ли способ действительно, серьезно вы, ребята, сделать x неизменяемым, чтобы ни один let x не мог переназначить его значение?

1 Ответ

0 голосов
/ 10 ноября 2018

Я считаю, что путаница заключается в том, что вы объединяете имена с хранилищем.

fn main() {
    let x = 5; // x_0
    println!("The value of x is {}", x);
    let x = 6; // x_1
    println!("The value of x is {}", x);
}

В этом примере есть одно имя (x) и два хранилища (x_0 и x_1). Второй let просто повторно связывает имя x для ссылки на место хранения x_1. * x_0 хранилище совершенно не затронуто.

fn main() {
    let mut x = 5; // x_0
    println!("The value of x is {}", x);
    x = 6;
    println!("The value of x is {}", x);
}

В этом примере есть одно имя (x) и одно хранилище (x_0). Назначение x = 6 напрямую изменяет биты места хранения x_0.

Вы можете утверждать, что они делают то же самое. Если так, то вы ошибаетесь:

fn main() {
    let x = 5; // x_0
    let y = &x; // y_0
    println!("The value of y is {}", y);
    let x = 6; // x_1
    println!("The value of y is {}", y);
}

Это выводит:

The value of y is 5
The value of y is 5

Это потому, что изменение того, к какому хранилищу x относится, абсолютно не влияет на хранилище x_0, на которое y_0 содержит указатель. Тем не менее,

fn main() {
    let mut x = 5; // x_0
    let y = &x; // y_0
    println!("The value of y is {}", y);
    x = 6;
    println!("The value of y is {}", y);
}

Это не скомпилируется, потому что вы не можете изменить x_0, пока он заимствован.

Rust заботится о защите от нежелательных мутационных эффектов , как указано в ссылках . Это не противоречит разрешению затенения, потому что вы не изменяете значения при затенении, вы просто меняете значение определенного имени таким образом, чтобы его нельзя было наблюдать где-либо еще. Затенение - это строго локальное изменение.

Так что, да, вы абсолютно можете сохранить значение x от изменения. То, что вы не можете сделать, это сохранить то, к чему относится имя x, от изменения. Самое большее, вы можете использовать что-то вроде clippy, чтобы отрицать затенение как пух.

...