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
BlockExpression
    {
        InnerAttribute*
        Statements?
    }

Statements
      Statement+
    | Statement+ ExpressionWithoutBlock
    | ExpressionWithoutBlock

Выражение-блок, или блок, — это выражение управления потоком выполнения и анонимная пространство имен для элементов и объявлений переменных.

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

Как анонимная пространство имен, объявления элементов видны только внутри самого блока, а переменные, объявленные операторами let, видны со следующего оператора до конца блока. Смотрите главу области видимости для более подробной информации.

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

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

  1. Операторы объявления элементов не требуют точки с запятой после себя.
  2. Операторы-выражения обычно требуют последующей точки с запятой, за исключением случая, когда их внешнее выражение является выражением управления потоком.

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

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

Затем выполняется конечный операнд, если он указан.

Тип блока — это тип конечного операнда или (), если конечный операнд опущен.

#![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
AsyncBlockExpressionasync 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
ConstBlockExpressionconst 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
UnsafeBlockExpressionunsafe 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() };
}

Блоки с метками

Блоки с метками документированы в разделе Циклы и другие прерываемые выражения.

Атрибуты на выражениях-блоках

Внутренние атрибуты разрешены непосредственно после открывающей фигурной скобки выражения-блока в следующих ситуациях:

Атрибуты, которые имеют смысл на выражении-блоке, это cfg и атрибуты проверки линтеров.

Например, эта функция возвращает true на unix платформах и false на других платформах.

#![allow(unused)]
fn main() {
fn is_unix_platform() -> bool {
    #[cfg(unix)] { true }
    #[cfg(not(unix))] { false }
}
}