Переменные и изменяемость

RustRustBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

Добро пожаловать в Переменные и изменяемость. Этот практикум является частью Книги о Rust. Вы можете практиковать навыки Rust в LabEx.

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


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/DataStructuresandEnumsGroup(["Data Structures and Enums"]) rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust(("Rust")) -.-> rust/DataTypesGroup(["Data Types"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/BasicConceptsGroup -.-> rust/mutable_variables("Mutable Variables") rust/BasicConceptsGroup -.-> rust/constants_usage("Constants Usage") rust/DataTypesGroup -.-> rust/integer_types("Integer Types") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") rust/DataStructuresandEnumsGroup -.-> rust/method_syntax("Method Syntax") subgraph Lab Skills rust/variable_declarations -.-> lab-100387{{"Переменные и изменяемость"}} rust/mutable_variables -.-> lab-100387{{"Переменные и изменяемость"}} rust/constants_usage -.-> lab-100387{{"Переменные и изменяемость"}} rust/integer_types -.-> lab-100387{{"Переменные и изменяемость"}} rust/function_syntax -.-> lab-100387{{"Переменные и изменяемость"}} rust/expressions_statements -.-> lab-100387{{"Переменные и изменяемость"}} rust/method_syntax -.-> lab-100387{{"Переменные и изменяемость"}} end

Переменные и изменяемость

Как упоминалось в разделе "Сохранение значений в переменных", по умолчанию переменные неизменяемы. Это один из многих подсказок, которые Rust дает вам для написания кода таким образом, чтобы воспользоваться безопасностью и простотой параллелизма, которые предлагает Rust. Однако, вы по-прежнему имеете возможность сделать свои переменные изменяемыми. Давайте рассмотрим, как и почему Rust поощряет вас к неизменяемости и почему иногда вы можете захотеть отказаться от нее.

Когда переменная неизменяемая,一旦 значение привязано к имени, вы не можете изменить это значение. Чтобы проиллюстрировать это, создайте новый проект под названием variables в вашей директории project с помощью команды cargo new variables.

Затем, в вашей новой директории variables, откройте src/main.rs и замените его код следующим кодом, который еще не скомпилируется:

Имя файла: src/main.rs

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

Сохраните и запустите программу с помощью cargo run. Вы должны получить сообщение об ошибке, связанной с ошибкой неизменяемости, как показано в этом выводе:

$ cargo run
   Compiling variables v0.1.0 (file:///projects/variables)
error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:4:5
  |
2 |     let x = 5;
  |         -
  |         |
  |         first assignment to `x`
  |         help: consider making this binding mutable: `mut x`
3 |     println!("The value of x is: {x}");
4 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable

Этот пример показывает, как компилятор помогает вам находить ошибки в ваших программах. Ошибки компилятора могут быть раздражающими, но на самом деле это означает только то, что ваша программа еще не безопасно делает то, что вы хотите, и не означает, что вы плохой программист! Опытные Rustaceans также получают ошибки компилятора.

Вы получили сообщение об ошибке cannot assign twice to immutable variablex`, потому что вы пытались присвоить второе значение неизменной переменнойx`.

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

Но изменяемость может быть очень полезной и может сделать код более удобным для написания. Хотя переменные по умолчанию неизменяемы, вы можете сделать их изменяемыми, добавив mut перед именем переменной, как вы делали в главе 2. Добавление mut также передаёт намерение будущим читателям кода, указывая, что другие части кода будут изменять значение этой переменной.

Например, давайте изменим src/main.rs на следующее:

Имя файла: src/main.rs

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

Теперь, когда мы запускаем программу, мы получаем следующий результат:

$ cargo run
   Compiling variables v0.1.0 (file:///projects/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.30s
     Running `target/debug/variables`
The value of x is: 5
The value of x is: 6

Мы можем изменить значение, привязанное к x, с 5 на 6, когда используется mut. В конечном итоге решение о том, использовать изменяемость или нет, зависит от вас и зависит от того, что вы считаете наиболее понятным в данной ситуации.

Константы

Похоже на неизменяемые переменные, константы - это значения, которые связаны с именем и не могут изменяться, но есть несколько отличий между константами и переменными.

Во - первых, вы не можете использовать mut с константами. Константы по умолчанию не только неизменяемы - они всегда неизменяемы. Константы объявляются с помощью ключевого слова const вместо let, и тип значения должен быть аннотирован. Мы поговорим о типах и аннотациях типов в разделе "Типы данных", так что не беспокойтесь о деталях сейчас. Просто запомните, что вы должны всегда аннотировать тип.

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

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

Вот пример объявления константы:

const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

Имя константы - THREE_HOURS_IN_SECONDS, и ее значение установлено равным результату умножения 60 (количество секунд в минуте) на 60 (количество минут в час) на 3 (количество часов, которое мы хотим подсчитать в этой программе). Rust соглашение о именовании констант - использовать все заглавные буквы с подчеркиваниями между словами. Компилятор может вычислить ограниченный набор операций во время компиляции, что позволяет нам выбирать способ записи этого значения, который легче понять и проверить, а не устанавливать эту константу равной значению 10800. См. раздел по оценке констант в Rust Reference по адресу https://doc.rust-lang.org/reference/const_eval.html для получения дополнительной информации о том, какие операции можно использовать при объявлении констант.

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

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

Скрытие (Shadowing)

Как вы видели в уроке по игре в угадывание в главе 2, вы можете объявить новую переменную с тем же именем, что и предыдущая переменная. Rustaceans говорят, что первая переменная скрывается (shadowed) второй, что означает, что вторая переменная - это то, что увидит компилятор, когда вы используете имя переменной. По сути, вторая переменная скрывает первую, захватывая любые использования имени переменной для себя, пока она сама не будет скрыта или не закончится область видимости. Мы можем скрыть переменную, используя то же имя переменной и повторяя использование ключевого слова let следующим образом:

Имя файла: src/main.rs

fn main() {
    let x = 5;

    let x = x + 1;

    {
        let x = x * 2;
        println!("The value of x in the inner scope is: {x}");
    }

    println!("The value of x is: {x}");
}

Эта программа сначала связывает x с значением 5. Затем она создает новую переменную x, повторяя let x =, беря исходное значение и добавляя 1, так что значение x становится 6. Затем, внутри области видимости, созданной с помощью фигурных скобок, третье объявление let также скрывает x и создает новую переменную, умножая предыдущее значение на 2, чтобы x получил значение 12. Когда эта область видимости заканчивается, внутреннее скрытие заканчивается и x возвращается к значению 6. Когда мы запускаем эту программу, она выведет следующее:

$ cargo run
   Compiling variables v0.1.0 (file:///projects/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.31s
     Running `target/debug/variables`
The value of x in the inner scope is: 12
The value of x is: 6

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

Другой разница между mut и скрытием заключается в том, что, так как мы фактически создаем новую переменную, когда снова используем ключевое слово let, мы можем изменить тип значения, но повторно использовать то же имя. Например, допустим, наша программа запрашивает у пользователя, сколько пробелов он хочет вставить между некоторым текстом, вводя пробельные символы, а затем мы хотим сохранить этот ввод в виде числа:

let spaces = "   ";
let spaces = spaces.len();

Первая переменная spaces имеет тип строки, а вторая переменная spaces имеет тип числа. Таким образом, скрытие позволяет избежать необходимости придумывать разные имена, такие как spaces_str и spaces_num; вместо этого мы можем повторно использовать более простое имя spaces. Однако, если мы попытаемся использовать mut для этого, как показано ниже, мы получим ошибку компиляции:

let mut spaces = "   ";
spaces = spaces.len();

Ошибка говорит, что мы не можем изменять тип переменной:

$ cargo run
   Compiling variables v0.1.0 (file:///projects/variables)
error[E0308]: mismatched types
 --> src/main.rs:3:14
  |
2 |     let mut spaces = "   ";
  |                      ----- expected due to this value
3 |     spaces = spaces.len();
  |              ^^^^^^^^^^^^ expected `&str`, found `usize`

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

Резюме

Поздравляем! Вы завершили практикум по Переменным и изменяемости. Вы можете выполнить больше практикумов в LabEx, чтобы улучшить свои навыки.