Выражения-блоки
Syntax
BlockExpression →
{
InnerAttribute*
Statements?
}
Statements →
Statement+
| Statement+ ExpressionWithoutBlock
| ExpressionWithoutBlock
Выражение-блок, или блок, — это выражение управления потоком выполнения и анонимная пространство имен для элементов и объявлений переменных.
Как выражение управления потоком, блок последовательно выполняет свои составные операторы не-элементы, а затем свое конечное необязательное выражение.
Как анонимная пространство имен, объявления элементов видны только внутри самого блока, а переменные, объявленные операторами let, видны со следующего оператора до конца блока.
Смотрите главу области видимости для более подробной информации.
Синтаксис блока: {, затем любые внутренние атрибуты, затем любое количество операторов, затем необязательное выражение, называемое конечным операндом, и, наконец, }.
Операторы обычно должны следовать за точкой с запятой, за двумя исключениями:
- Операторы объявления элементов не требуют точки с запятой после себя.
- Операторы-выражения обычно требуют последующей точки с запятой, за исключением случая, когда их внешнее выражение является выражением управления потоком.
Кроме того, дополнительные точки с запятой между операторами разрешены, но они не влияют на семантику.
При вычислении выражения-блока каждый оператор, кроме операторов объявления элементов, выполняется последовательно.
Затем выполняется конечный операнд, если он указан.
Тип блока — это тип конечного операнда или (), если конечный операнд опущен.
#![allow(unused)] fn main() { fn fn_call() {} let _: () = { fn_call(); }; let five: i32 = { fn_call(); 5 }; assert_eq!(5, five); }
Note
Как выражение управления потоком, если выражение-блок является внешним выражением оператора-выражения, ожидаемый тип —
(), если за ним не следует сразу точка с запятой.
Блоки всегда являются выражениями-значениями и вычисляют последний операнд в контексте выражения-значения.
Note
Это можно использовать для принудительного перемещения значения, если это действительно необходимо. Например, следующий пример завершается ошибкой при вызове
consume_self, потому что структура была перемещена изsв выражении-блоке.#![allow(unused)] fn main() { struct Struct; impl Struct { fn consume_self(self) {} fn borrow_self(&self) {} } fn move_by_block_expression() { let s = Struct; // Перемещаем значение из `s` в выражении-блоке. (&{ s }).borrow_self(); // Не выполняется, потому что `s` была перемещена. s.consume_self(); } }
async блоки
Syntax
AsyncBlockExpression → async move? BlockExpression
Async блок — это вариант выражения-блока, которое вычисляется в будущее (future).
Конечное выражение блока, если оно присутствует, определяет результирующее значение будущего.
Выполнение async блока похоже на выполнение выражения замыкания: его непосредственный эффект — создание и возврат анонимного типа.
Однако, в то время как замыкания возвращают тип, который реализует один или более трейтов std::ops::Fn, тип, возвращаемый для async блока, реализует трейт std::future::Future.
Фактуальный формат данных для этого типа не специфицирован.
Note
Тип будущего, генерируемый rustc, грубо эквивалентен перечислению с одним вариантом на каждую точку
await, где каждый вариант хранит данные, необходимые для возобновления из соответствующей точки.
2018 Edition differences
Async блоки доступны только начиная с Rust 2018.
Режимы захвата
Async блоки захватывают переменные из своего окружения, используя те же режимы захвата, что и замыкания.
Как и замыкания, когда написан async { .. }, режим захвата для каждой переменной будет выведен из содержимого блока.
Однако блоки async move { .. } будут перемещать все referenced переменные в результирующее будущее.
Async контекст
Поскольку async блоки конструируют будущее, они определяют async контекст, который, в свою очередь, может содержать await выражения.
Async контексты устанавливаются async блоками, а также телами async функций, чья семантика определена в терминах async блоков.
Операторы управления потоком
Async блоки действуют как граница функции, подобно замыканиям.
Поэтому оператор ? и выражения return влияют на вывод будущего, а не на объемлющую функцию или другой контекст.
То есть return <expr> изнутри async блока вернет результат <expr> как вывод будущего.
Аналогично, если <expr>? распространяет ошибку, эта ошибка распространяется как результат будущего.
Наконец, ключевые слова break и continue не могут быть использованы для выхода из async блока.
Следовательно, следующее незаконно:
#![allow(unused)] fn main() { loop { async move { break; // ошибка[E0267]: `break` внутри `async` блока } } }
const блоки
Syntax
ConstBlockExpression → const BlockExpression
Const блок — это вариант выражения-блока, тело которого вычисляется во время компиляции, а не во время выполнения.
Const блоки позволяют определить константное значение без необходимости определять новые константные элементы, и поэтому их иногда называют встроенными константами (inline consts). Они также поддерживают вывод типов, поэтому не нужно указывать тип, в отличие от константных элементов.
Const блоки имеют возможность ссылаться на обобщенные параметры в области видимости, в отличие от свободных константных элементов. Они десугарируются в константные элементы с обобщенными параметрами в области видимости (аналогично ассоциированным константам, но без трейта или типа, с которым они ассоциированы). Например, этот код:
#![allow(unused)] fn main() { fn foo<T>() -> usize { const { std::mem::size_of::<T>() + 1 } } }
эквивалентен:
#![allow(unused)] fn main() { fn foo<T>() -> usize { { struct Const<T>(T); impl<T> Const<T> { const CONST: usize = std::mem::size_of::<T>() + 1; } Const::<T>::CONST } } }
Если выражение const блока выполняется во время выполнения, то гарантируется, что константа будет вычислена, даже если ее возвращаемое значение игнорируется:
#![allow(unused)] fn main() { fn foo<T>() -> usize { // Если этот код когда-либо выполнится, то утверждение точно // было вычислено во время компиляции. const { assert!(std::mem::size_of::<T>() > 0); } // Здесь у нас может быть небезопасный код, полагающийся на то, что тип имеет ненулевой размер. /* ... */ 42 } }
Если выражение const блока не выполняется во время выполнения, оно может быть вычислено или нет:
#![allow(unused)] fn main() { if false { // Паника может произойти или не произойти при сборке программы. const { panic!(); } } }
unsafe блоки
Syntax
UnsafeBlockExpression → unsafe BlockExpression
Смотрите unsafe блоки для получения дополнительной информации о том, когда использовать unsafe.
Блок кода может быть предварен ключевым словом unsafe, чтобы разрешить небезопасные операции.
Примеры:
#![allow(unused)] fn main() { unsafe { let b = [13u8, 17u8]; let a = &b[0] as *const u8; assert_eq!(*a, 13); assert_eq!(*a.offset(1), 17); } unsafe fn an_unsafe_fn() -> i32 { 10 } let a = unsafe { an_unsafe_fn() }; }
Блоки с метками
Блоки с метками документированы в разделе Циклы и другие прерываемые выражения.
Атрибуты на выражениях-блоках
Внутренние атрибуты разрешены непосредственно после открывающей фигурной скобки выражения-блока в следующих ситуациях:
- Тела функций и методов.
- Тела циклов (
loop,while, иfor). - Выражения-блоки, используемые как оператор.
- Выражения-блоки как элементы выражений-массивов, выражений-кортежей, выражений-вызовов и структурных выражений в стиле кортежей.
- Выражение-блок как конечное выражение другого выражения-блока.
Атрибуты, которые имеют смысл на выражении-блоке, это cfg и атрибуты проверки линтеров.
Например, эта функция возвращает true на unix платформах и false на других платформах.
#![allow(unused)] fn main() { fn is_unix_platform() -> bool { #[cfg(unix)] { true } #[cfg(not(unix))] { false } } }