При затенении имени вместо присваивания, что происходит с прежним владельцем значения?
При затенении создаётся новая переменная с тем же именем, а прежняя переменная остаётся отдельным binding и больше недоступна по этому имени. Её значение не уничтожается автоматически только из-за затенения: оно либо продолжает принадлежать прежнему binding до его drop, либо передаётся новой переменной через явное перемещение.
При присваивании новая переменная не создаётся: существующий binding получает новое значение, а его прежнее значение уничтожается, если оно не было предварительно перемещено или иным образом освобождено.
Затенение удобно в языках с неизменяемыми binding: оно позволяет последовательно преобразовывать значение, не вводя новые имена вроде parsed_value или validated_value. В Rust это особенно полезно, потому что переменная может менять тип при затенении, сохраняя понятное имя.
Такой подход также поддерживает модель владения: каждое объявление имеет собственный binding и собственный жизненный цикл. Это отличается от мутации существующей переменной, где нужно явно объявить её изменяемой.
Если спутать затенение с присваиванием, можно неверно определить, когда освобождается ресурс и какой binding владеет значением. Для типов вроде String, Vec или файлового дескриптора это влияет на момент вызова Drop и на возможность дальнейшего использования переменной.
Особенно важно различать два случая: новое объявление может переместить значение из старой переменной, а может получить полностью новое значение. Во втором случае старое и новое значения существуют независимо и будут освобождены отдельно.
При затенении let создаёт новый binding. Старый binding перестаёт быть доступен по имени в последующем коде, но это не означает немедленного присваивания или автоматического перемещения его значения.
Если новое объявление использует старое значение справа, происходит обычный move. После этого старый binding становится недействительным, а новый binding становится владельцем того же значения. Если справа создаётся другое значение, старый binding сохраняет владение своим прежним значением до завершения области действия или более раннего безопасного освобождения.
При присваивании оператор записи работает с уже существующим binding. Сначала вычисляется новое значение, затем прежнее значение этого binding заменяется и обычно уничтожается. Присваивание не позволяет изменить тип переменной, тогда как затенение позволяет:
В первой последовательности второй text — новый binding типа usize; исходный String был перемещён в выражение нового объявления и затем освобождён по правилам владения. Во второй последовательности name остаётся тем же binding типа String, а его прежнее содержимое заменяется новым.
Затенение не является способом принудительно освободить ресурс в произвольный момент. Если точный момент освобождения важен, применяют отдельную область действия или явный вызов drop, учитывая, что после drop значение использовать нельзя.
Допустим, обработчик получает строку, затем должен разобрать её в число и использовать одно имя на всех этапах. Затенение позволяет сначала владеть String, затем связать с тем же именем результат преобразования другого типа. Присваивание для этого не подходит: тип существующей переменной менять нельзя.
Альтернатива — создать несколько имён, например raw_text, parsed_number и validated_number. Такой вариант лучше, если нужно одновременно обращаться к промежуточным значениям, но он увеличивает количество binding и может усложнить чтение короткого линейного преобразования.
Практический выбор: использовать затенение для последовательного преобразования, когда старое значение больше не нужно, и разные имена, когда промежуточные результаты должны жить одновременно. Это сохраняет ясную модель владения и не требует лишней изменяемости.
1. Всегда ли затенение немедленно уничтожает значение старой переменной?
Нет. Затенение скрывает старый binding, но само по себе не означает присваивание и не обязано немедленно вызывать Drop. Если новое объявление получает независимое значение, старое значение по-прежнему принадлежит прежнему binding и освобождается по правилам его области действия. Компилятор может учитывать фактическую доступность и жизненный цикл, но полагаться на затенение как на точный механизм досрочного освобождения не следует.
2. Что происходит при записи let новое_имя = старое_имя для значения, не реализующего Copy?
Происходит перемещение. Новый binding получает владение значением, а старый binding становится недействительным и не может использоваться для чтения или повторного перемещения. Это отличается от объявления нового имени с независимым конструктором значения: в таком случае старое значение никуда не перемещается.
3. Может ли затенение изменить тип и почему присваивание этого не позволяет?
Да. Затенение создаёт новый binding, поэтому его тип выводится независимо от типа предыдущего binding. Присваивание обращается к уже созданной переменной, а тип переменной Rust фиксирован после инициализации; новое выражение должно быть совместимо с этим типом. Это одна из наиболее заметных практических границ между затенением и мутацией.