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
ConstantItem
    const ( IDENTIFIER | _ ) : Type ( = Expression )? ;

Константный элемент - это опционально именованное константное значение, которое не связано с конкретным местом в памяти программы.

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

Объявление константы определяет константное значение в пространстве имён значений модуля или блока, где она находится.

Константы должны быть явно типизированы. Тип должен иметь время жизни 'static: любые ссылки в инициализаторе должны иметь время жизни 'static. Ссылки в типе константы по умолчанию имеют время жизни 'static; см. статическое элизирование времени жизни.

Ссылка на константу будет иметь время жизни 'static, если значение константы подходит для продвижения; в противном случае будет создана временная переменная.

#![allow(unused)]
fn main() {
const BIT1: u32 = 1 << 0;
const BIT2: u32 = 1 << 1;

const BITS: [u32; 2] = [BIT1, BIT2];
const STRING: &'static str = "bitstring";

struct BitsNStrings<'a> {
    mybits: [u32; 2],
    mystring: &'a str,
}

const BITS_N_STRINGS: BitsNStrings<'static> = BitsNStrings {
    mybits: BITS,
    mystring: STRING,
};
}

Финальное значение элемента const, после вычисления инициализатора до значения, которое имеет объявленный тип константы, не может содержать никаких изменяемых ссылок, кроме случаев, описанных ниже.

#![allow(unused)]
fn main() {
#![allow(static_mut_refs)]
static mut S: u8 = 0;
const _: &u8 = unsafe { &mut S }; // OK.
//                      ^^^^^^
// Разрешено, так как это приводится к `&S`.
}
#![allow(unused)]
fn main() {
use core::sync::atomic::AtomicU8;
static S: AtomicU8 = AtomicU8::new(0);
const _: &AtomicU8 = &S; // OK.
//                   ^^
// Разрешено, даже несмотря на то, что разделяемая ссылка указывает на внутренне
// изменяемое значение.
}
#![allow(unused)]
fn main() {
#![allow(static_mut_refs)]
static mut S: u8 = 0;
const _: &mut u8 = unsafe { &mut S }; // ОШИБКА.
//                          ^^^^^^
// Не разрешено, так как изменяемая ссылка появляется в финальном значении.
}

Note

Инициализаторы констант можно рассматривать, в большинстве случаев, как встраиваемые везде, где появляется константа. Если бы константа, значение которой содержит изменяемую ссылку на изменяемую статическую переменную, появлялась дважды, и это было бы разрешено, это создало бы две изменяемые ссылки, каждая с временем жизни 'static, на одно и то же место. Это могло бы привести к неопределённому поведению.

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

#![allow(unused)]
fn main() {
const _: &mut u8 = &mut 0; // ОШИБКА.
//                 ^^^^^^
// Не разрешено, так как изменяемая ссылка появляется в финальном значении и
// потому что константное выражение содержит изменяемое заимствование
// выражения, чья временная область видимости была бы расширена до конца
// программы.
}

Здесь значение 0 является временной переменной, чья область видимости расширена до конца программы (см. destructors.scope.lifetime-extension.static). Такие временные переменные не могут быть изменяемо заимствованы в константных выражениях (см. const-eval.const-expr.borrows).

Чтобы разрешить это, нам пришлось бы решить, создаёт ли каждое использование константы новое значение u8 или каждое использование разделяет одну и ту же временную переменную с расширенным временем жизни. Последний выбор, хотя ближе к тому, как rustc думает об этом сегодня, нарушил бы концептуальную модель, что в большинстве случаев инициализатор константы можно рассматривать как встраиваемый везде, где используется константа. Поскольку мы не решили, и из-за другой упомянутой проблемы, это не разрешено.

#![allow(unused)]
fn main() {
#![allow(static_mut_refs)]
static mut S: u8 = 0;
const _: &dyn Send = &unsafe { &mut S }; // ОШИБКА.
//                             ^^^^^^
// Не разрешено, так как изменяемая ссылка появляется в финальном значении,
// даже несмотря на стирание типа.
}

Изменяемые ссылки, где указываемый объект является значением типа нулевого размера, разрешены.

#![allow(unused)]
fn main() {
#![allow(static_mut_refs)]
static mut S: () = ();
const _: &mut () = unsafe { &mut S }; // OK.
//            ^^ Это тип нулевого размера.
}
#![allow(unused)]
fn main() {
#![allow(static_mut_refs)]
static mut S: [u8; 0] = [0; 0];
const _: &mut [u8; 0] = unsafe { &mut S }; // OK.
//            ^^^^^^^ Это тип нулевого размера.
}

Note

Это разрешено, так как для значения типа нулевого размера никакие байты не могут быть фактически изменены. Мы должны принять это, так как &mut [] продвигается.

Значения типа объединения не считаются содержащими какие-либо ссылки; для этой цели значение типа объединения обрабатывается как последовательность нетипизированных байтов.

#![allow(unused)]
fn main() {
#![allow(static_mut_refs)]
union U { f: &'static mut u8 }
static mut S: u8 = 0;
const _: U = unsafe { U { f: &mut S }}; // OK.
//                    ^^^^^^^^^^^^^^^
// Это обрабатывается как последовательность нетипизированных байтов.
}

Изменяемые ссылки, содержащиеся внутри изменяемой статической переменной, могут быть ссылаемы в финальном значении константы.

#![allow(unused)]
fn main() {
#![allow(static_mut_refs)]
static mut S: &mut u8 = unsafe { static mut I: u8 = 0; &mut I };
const _: &&mut u8 = unsafe { &S }; // OK.
//        ^^^^^^^
// Эта изменяемая ссылка происходит из `static mut`.
}

Note

Это разрешено, так как отдельно не разрешено читать из изменяемой статической переменной во время вычисления константы. См. const-eval.const-expr.path-static.

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

#![allow(unused)]
fn main() {
#![allow(static_mut_refs)]
unsafe extern "C" { static S: &'static mut u8; }
const _: &&mut u8 = unsafe { &S }; // OK.
//        ^^^^^^^
// Эта изменяемая ссылка происходит из внешней статической переменной.
}

Note

Это разрешено, так как отдельно не разрешено читать из внешней статической переменной во время вычисления константы. См. const-eval.const-expr.path-static.

Note

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

#![allow(unused)]
fn main() {
use core::sync::atomic::AtomicU8;
static S: AtomicU8 = AtomicU8::new(0);
const _: &AtomicU8 = &S; // OK.
}

Однако мы запрещаем аналогичный код, когда внутренне изменяемое значение создаётся в инициализаторе.

#![allow(unused)]
fn main() {
use core::sync::atomic::AtomicU8;
const _: &AtomicU8 = &AtomicU8::new(0); // ОШИБКА.
}

Здесь AtomicU8 является временной переменной, чья область видимости расширена до конца программы (см. destructors.scope.lifetime-extension.static). Такие временные переменные с внутренней изменяемостью не могут быть заимствованы в константных выражениях (см. const-eval.const-expr.borrows).

Чтобы разрешить это, нам пришлось бы решить, создаёт ли каждое использование константы новый AtomicU8 или каждое использование разделяет одну и ту же временную переменную с расширенным временем жизни. Последний выбор, хотя ближе к тому, как rustc думает об этом сегодня, нарушил бы концептуальную модель, что в большинстве случаев инициализатор константы можно рассматривать как встраиваемый везде, где используется константа. Поскольку мы не решили, это не разрешено.

Константное выражение может быть опущено только в определении трейта.

Константы с деструкторами

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

#![allow(unused)]
fn main() {
struct TypeWithDestructor(i32);

impl Drop for TypeWithDestructor {
    fn drop(&mut self) {
        println!("Dropped. Held {}.", self.0);
    }
}

const ZERO_WITH_DESTRUCTOR: TypeWithDestructor = TypeWithDestructor(0);

fn create_and_drop_zero_with_destructor() {
    let x = ZERO_WITH_DESTRUCTOR;
    // x удаляется в конце функции, вызывая drop.
    // выводит "Dropped. Held 0.".
}
}

Безымянные константы

В отличие от ассоциированной константы, свободная константа может быть безымянной с использованием подчёркивания вместо имени. Например:

#![allow(unused)]
fn main() {
const _: () =  { struct _SameNameTwice; };

// OK, хотя это то же имя, что и выше:
const _: () =  { struct _SameNameTwice; };
}

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

#![allow(unused)]
fn main() {
macro_rules! m {
    ($item: item) => { $item $item }
}

m!(const _: () = (););
// Это раскрывается в:
// const _: () = ();
// const _: () = ();
}

Вычисление

Свободные константы всегда вычисляются во время компиляции для выявления паник. Это происходит даже внутри неиспользуемой функции:

#![allow(unused)]
fn main() {
// Паника во время компиляции
const PANIC: () = std::unimplemented!();

fn unused_generic_function<T>() {
    // Неудачная проверка во время компиляции
    const _: () = assert!(usize::BITS == 0);
}
}