Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Выражения

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) и выражения мест в скобках.

Все остальные выражения являются выражениями значений.

Выражение значения — это выражение, которое представляет фактическое значение.

Следующие контексты являются контекстами выражений мест:

Note

Исторически выражения мест назывались lvalues, а выражения значений назывались rvalues.

Выражение присваивания — это выражение, которое появляется в левом операнде выражения присваивания. Явно, выражения присваивания это:

Произвольная расстановка скобок разрешена внутри выражений присваивания.

Перемещаемые и копируемые типы

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

Если тип этого значения реализует Copy, то значение будет скопировано.

В оставшихся ситуациях, если этот тип Sized, то может быть возможно переместить значение.

Только следующие выражения мест могут быть перемещены:

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

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

Изменяемость

Чтобы выражение места могло быть присвоено, изменяемо заимствовано, неявно изменяемо заимствовано или привязано к паттерну, содержащему 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);
}

Неявные заимствования могут быть взяты в следующих выражениях:

Перегружаемые трейты

Многие из следующих операторов и выражений также могут быть перегружены для других типов с использованием трейтов в std::ops или std::cmp. Эти трейты также существуют в core::ops и core::cmp с теми же именами.

Атрибуты выражений

Внешние атрибуты перед выражением разрешены только в нескольких конкретных случаях:

Они никогда не разрешены перед: