#rust
#Ржавчина
Вопрос:
Пример кода:
use std::sync::atomic::{AtomicU32, Ordering};
#[derive(Debug)]
struct Token(u32);
impl Token {
fn new() -> Self {
static COUNTER: AtomicU32 = AtomicU32::new(1);
let inner = COUNTER.fetch_add(1, Ordering::Relaxed);
Token(inner)
}
}
fn main() {
let t1 = Token::new();
let t2 = Token::new();
let t3 = Token::new();
println!("{:?}n{:?}n{:?}", t1, t2, t3);
}
Когда я запускаю фрагмент кода, показанный выше, он выводит:
Token(1)
Token(2)
Token(3)
Я нашел в ссылке на Rust, что инициализация статических элементов оценивается во время компиляции.
Интересно, что именно происходит во время выполнения, когда программа выполняется до строки, которая инициализирует переменную COUNTER
. Как выглядит скомпилированный код, чтобы он мог пренебречь инициализацией?
Ответ №1:
static
переменные обрабатываются одинаково, независимо от того, определены ли они на уровне модуля или на уровне функции. Единственное отличие заключается в области разрешения имен.
Многие форматы файлов для исполняемых файлов, включая ELF и PE, структурируют программы в различные разделы. Обычно код для функций находится в одном разделе, изменяемые глобальные переменные — в другом разделе, а константы (включая строковые литералы) — в еще одном разделе. Когда операционная система загружает программу в память, эти разделы отображаются в память с различными параметрами защиты памяти (например, константы недоступны для записи), как указано в исполняемом файле.
Когда в документации говорится, что static
элементы инициализируются во время компиляции, это означает, что компилятор определяет начальное значение этого элемента во время компиляции, а затем записывает это значение в соответствующий раздел скомпилированного двоичного файла. При запуске вашей программы значение уже будет в памяти, прежде чем ваша программа получит возможность выполнить хотя бы одну инструкцию.
Компилятор может вычислить выражение AtomicU32::new(1)
, поскольку AtomicU32::new
оно определено как const fn
. Добавление const
к определению функции означает, что ее можно использовать в выражениях, которые вычисляются во время компиляции, но const fn
они намного более ограничены, чем обычные функции, в том, что они могут делать. В случае AtomicU32::new
though все, что нужно сделать функции, это инициализировать AtomicU32
структуру, которая является просто оболочкой вокруг a u32
.