Выражения
Syntax
Expression →
ExpressionWithoutBlock
| ExpressionWithBlock
ExpressionWithoutBlock →
OuterAttribute*
(
LiteralExpression
| PathExpression
| OperatorExpression
| GroupedExpression
| ArrayExpression
| AwaitExpression
| IndexExpression
| TupleExpression
| TupleIndexingExpression
| StructExpression
| CallExpression
| MethodCallExpression
| FieldExpression
| ClosureExpression
| AsyncBlockExpression
| ContinueExpression
| BreakExpression
| RangeExpression
| ReturnExpression
| UnderscoreExpression
| MacroInvocation
)
ExpressionWithBlock →
OuterAttribute*
(
BlockExpression
| ConstBlockExpression
| UnsafeBlockExpression
| LoopExpression
| IfExpression
| MatchExpression
)
Выражение может иметь две роли: оно всегда производит значение и может иметь эффекты (также известные как “побочные эффекты”).
Выражение вычисляется в значение и имеет эффекты во время вычисления.
Многие выражения содержат подвыражения, называемые операндами выражения.
Значение каждого вида выражения диктует несколько вещей:
- Вычислять ли операнды при вычислении выражения
- Порядок вычисления операндов
- Как комбинировать значения операндов для получения значения выражения
Таким образом, структура выражений диктует структуру выполнения. Блоки — это просто другой вид выражений, поэтому блоки, операторы, выражения и снова блоки могут рекурсивно вкладываться друг в друга на произвольную глубину.
Note
Мы даём имена операндам выражений, чтобы мы могли обсуждать их, но эти имена не стабильны и могут быть изменены.
Приоритет выражений
Приоритет операторов и выражений Rust упорядочен следующим образом, от сильного к слабому. Бинарные операторы на одном уровне приоритета группируются в порядке, заданном их ассоциативностью.
| Оператор/Выражение | Ассоциативность |
|---|---|
| Пути | |
| Вызовы методов | |
| Выражения полей | слева направо |
| Вызовы функций, индексация массивов | |
? | |
Унарные - ! * заимствование | |
as | слева направо |
* / % | слева направо |
+ - | слева направо |
<< >> | слева направо |
& | слева направо |
^ | слева направо |
| | слева направо |
== != < > <= >= | Требуют скобок |
&& | слева направо |
|| | слева направо |
.. ..= | Требуют скобок |
= += -= *= /= %= &= |= ^= <<= >>= | справа налево |
return break замыкания |
Порядок вычисления операндов
Следующий список выражений вычисляет свои операнды одинаковым образом, как описано после списка. Другие выражения либо не принимают операнды, либо вычисляют их условно, как описано на соответствующих страницах.
- Выражение разыменования
- Выражение распространения ошибки
- Выражение отрицания
- Арифметические и логические бинарные операторы
- Операторы сравнения
- Выражение приведения типа
- Группированное выражение
- Выражение массива
- Выражение await
- Выражение индекса
- Выражение кортежа
- Выражение индекса кортежа
- Выражение структуры
- Выражение вызова
- Выражение вызова метода
- Выражение поля
- Выражение break
- Выражение диапазона
- Выражение return
Операнды этих выражений вычисляются до применения эффектов выражения. Выражения, принимающие несколько операндов, вычисляются слева направо, как написано в исходном коде.
Note
Какие подвыражения являются операндами выражения, определяется приоритетом выражений согласно предыдущему разделу.
Например, два вызова метода next всегда будут вызываться в одном и том же порядке:
#![allow(unused)] fn main() { // Использование vec вместо массива, чтобы избежать ссылок // поскольку на момент написания этого примера не было // стабильного итератора по владеемому массиву. let mut one_two = vec![1, 2].into_iter(); assert_eq!( (1, 2), (one_two.next().unwrap(), one_two.next().unwrap()) ); }
Note
Поскольку это применяется рекурсивно, эти выражения также вычисляются от самого внутреннего к самому внешнему, игнорируя соседние элементы, пока не останется внутренних подвыражений.
Выражения мест и выражения значений
Выражения делятся на две основные категории: выражения мест и выражения значений; есть также третья, второстепенная категория выражений, называемая выражениями присваивания. Внутри каждого выражения операнды могут аналогично встречаться либо в контексте места, либо в контексте значения. Вычисление выражения зависит как от его собственной категории, так и от контекста, в котором оно встречается.
Выражение места — это выражение, которое представляет область памяти.
Эти выражения — это пути, которые ссылаются на локальные переменные, статические переменные, разыменования (*expr), выражения индексации массивов (expr[expr]), ссылки на поля (expr.f) и выражения мест в скобках.
Все остальные выражения являются выражениями значений.
Выражение значения — это выражение, которое представляет фактическое значение.
Следующие контексты являются контекстами выражений мест:
- Левый операнд выражения составного присваивания.
- Операнд унарного оператора заимствования, сырого заимствования или разыменования.
- Операнд выражения поля.
- Индексируемый операнд выражения индексации массива.
- Операнд любого неявного заимствования.
- Инициализатор оператора let.
- Образец выражения
if let,matchилиwhile let. - Основа функционального обновления структуры.
Note
Исторически выражения мест назывались lvalues, а выражения значений назывались rvalues.
Выражение присваивания — это выражение, которое появляется в левом операнде выражения присваивания. Явно, выражения присваивания это:
- Выражения мест.
- Подчёркивания.
- Кортежи из выражений присваивания.
- Срезы из выражений присваивания.
- Кортежные структуры из выражений присваивания.
- Структуры из выражений присваивания (с опционально именованными полями).
- Unit структуры
Произвольная расстановка скобок разрешена внутри выражений присваивания.
Перемещаемые и копируемые типы
Когда выражение места вычисляется в контексте выражения значения или привязывается по значению в паттерне, оно обозначает значение, хранящееся в этой области памяти.
Если тип этого значения реализует Copy, то значение будет скопировано.
В оставшихся ситуациях, если этот тип Sized, то может быть возможно переместить значение.
Только следующие выражения мест могут быть перемещены:
- Переменные, которые в настоящее время не заимствованы.
- Временные значения.
- Поля выражения места, которые можно переместить и которые не реализуют
Drop. - Результат разыменования выражения с типом
Box<T>, которое также можно переместить.
После перемещения из выражения места, которое вычисляется в локальную переменную, местоположение деинициализируется и не может быть прочитано снова, пока не будет реинициализировано.
Во всех других случаях попытка использовать выражение места в контексте выражения значения является ошибкой.
Изменяемость
Чтобы выражение места могло быть присвоено, изменяемо заимствовано, неявно изменяемо заимствовано или привязано к паттерну, содержащему ref mut, оно должно быть изменяемым.
Мы называем их изменяемыми выражениями мест.
В противоположность этому, другие выражения мест называются неизменяемыми выражениями мест.
Следующие выражения могут быть контекстами изменяемых выражений мест:
- Изменяемые переменные, которые в настоящее время не заимствованы.
- Изменяемые
staticэлементы. - Временные значения.
- Поля: это вычисляет подвыражение в контексте изменяемого выражения места.
- Разыменования указателя
*mut T. - Разыменование переменной или поля переменной с типом
&mut T. Примечание: Это исключение из требования следующего правила. - Разыменования типа, который реализует
DerefMut: это затем требует, чтобы значение, которое разыменовывается, вычислялось в контексте изменяемого выражения места. - Индексация массива типа, который реализует
IndexMut: это затем вычисляет значение, которое индексируется, но не индекс, в контексте изменяемого выражения места.
Временные значения
При использовании выражения значения в большинстве контекстов выражений мест создаётся временное безымянное местоположение памяти и инициализируется этим значением.
Выражение вычисляется в это местоположение вместо этого, за исключением случаев, когда оно повышено до static.
Область удаления временного значения обычно является концом охватывающего оператора.
Супер макросы
Некоторые встроенные макросы могут создавать временные значения, чьи области видимости могут быть расширены. Эти временные значения являются супер временными, а эти макросы — супер макросами. Вызовы этих макросов являются выражениями вызова супер макросов. Аргументы этих макросов могут быть супер операндами.
Note
Когда выражение вызова супер макроса является расширяющим выражением, его супер операнды являются расширяющими выражениями и области видимости супер временных значений расширены. См. destructors.scope.lifetime-extension.exprs.
format_args!
За исключением аргумента строки формата, все аргументы, переданные в format_args!, являются супер операндами.
#![allow(unused)] fn main() { fn temp() -> String { String::from("") } // Поскольку вызов является расширяющим выражением и аргумент // является супер операндом, внутренний блок является расширяющим выражением, // поэтому область видимости временного значения, созданного в его завершающем выражении, // расширена. let _ = format_args!("{}", { &temp() }); // OK }
Супер операнды format_args! неявно заимствуются и поэтому являются контекстами выражений мест. Когда выражение значения передаётся как аргумент, оно создаёт супер временное значение.
#![allow(unused)] fn main() { fn temp() -> String { String::from("") } let x = format_args!("{}", temp()); x; // <-- Временное значение расширено, позволяя использование здесь. }
Развёртывание вызова format_args! иногда создаёт другие внутренние супер временные значения.
#![allow(unused)] fn main() { let x = { // Этот вызов создаёт внутреннее временное значение. let x = format_args!("{:?}", 0); x // <-- Временное значение расширено, позволяя его использование здесь. }; // <-- Временное значение удаляется здесь. x; // ERROR }
#![allow(unused)] fn main() { // Этот вызов не создаёт внутреннее временное значение. let x = { let x = format_args!("{}", 0); x }; x; // OK }
Note
Подробности того, когда
format_args!создаёт или не создаёт внутренние временные значения, в настоящее время не специфицированы.
pin!
Аргумент pin! является супер операндом.
#![allow(unused)] fn main() { use core::pin::pin; fn temp() {} // Как выше для `format_args!`. let _ = pin!({ &temp() }); // OK }
Аргумент pin! является контекстом выражения значения и создаёт супер временное значение.
#![allow(unused)] fn main() { use core::pin::pin; fn temp() {} // Аргумент вычисляется в супер временное значение. let x = pin!(temp()); // Временное значение расширено, позволяя его использование здесь. x; // OK }
Неявные заимствования
Некоторые выражения будут обрабатывать выражение как выражение места, неявно заимствуя его.
Например, можно напрямую сравнивать два несized среза на равенство, потому что оператор == неявно заимствует свои операнды:
#![allow(unused)] fn main() { let c = [1, 2, 3]; let d = vec![1, 2, 3]; let a: &[i32]; let b: &[i32]; a = &c; b = &d; // ... *a == *b; // Эквивалентная форма: ::std::cmp::PartialEq::eq(&*a, &*b); }
Неявные заимствования могут быть взяты в следующих выражениях:
- Левый операнд в выражениях вызова метода.
- Левый операнд в выражениях поля.
- Левый операнд в выражениях вызова.
- Левый операнд в выражениях индексации массива.
- Операнд оператора разыменования (
*). - Операнды сравнения.
- Левые операнды составного присваивания.
- Аргументы
format_args!кроме строки формата.
Перегружаемые трейты
Многие из следующих операторов и выражений также могут быть перегружены для других типов с использованием трейтов в std::ops или std::cmp.
Эти трейты также существуют в core::ops и core::cmp с теми же именами.
Атрибуты выражений
Внешние атрибуты перед выражением разрешены только в нескольких конкретных случаях:
- Перед выражением, используемым как оператор.
- Элементы выражений массивов, кортежей, вызовов и кортежных структур.
- Завершающее выражение блочных выражений.
Они никогда не разрешены перед:
- Выражениями диапазона.
- Выражениями бинарных операторов (ArithmeticOrLogicalExpression, ComparisonExpression, LazyBooleanExpression, TypeCastExpression, AssignmentExpression, CompoundAssignmentExpression).