Как указать константный массив в глобальной области видимости в Rust?
Проблема: почему ваш код не компилируется?
В Rust const и static — это разные вещи, и оба имеют строгие ограничения. Ваш код не работает по нескольким причинам:
- Для
const: Значение должно быть вычислено на этапе компиляции. Строковые литералы (&str) — это ссылки на область памяти, которая ещё не выделена на этапе компиляции в глобальном контексте. Rust не позволяетconstсодержать ссылки, которые не являются'staticявно. - Для
static: Статические переменные должны бытьSync(безопасны для доступа из нескольких потоков).&strреализуетSync, но проблема в том, чтоstaticне позволяет использовать неконстантные выражения для инициализации в некоторых контекстах.
Решение: используйте static с явным указанием времени жизни
Правильный способ объявить глобальный константный массив строк:
static DAYS: [&'static str; 7] = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"];
Ключевые моменты:
static— переменная, которая существует всё время работы программы (имеет статическое время жизни).&'static str— ссылка на строку, которая тоже живёт всё время работы программы. Строковые литералы в Rust автоматически имеют время жизни'static.- Массив
[&'static str; 7]— это массив из 7 таких ссылок.
Альтернатива: используйте const с примитивными типами
Если ваш массив состоит из чисел или других примитивов, const работает отлично:
const FIBONACCI: [u32; 10] = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34];
fn main() {
println!("Третье число Фибоначчи: {}", FIBONACCI[2]);
}
Почему const не подходит для строк?
В Rust const — это не переменная, а константа времени компиляции. Каждый раз, когда вы используете const, компилятор вставляет её значение прямо в код (как макрос). Для ссылок это проблематично, потому что компилятор не может гарантировать, что ссылка останется валидной во всех контекстах. static же — это реальная переменная в памяти, и Rust может управлять её временем жизни.
Полный рабочий пример
static DAYS: [&'static str; 7] = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"];
fn get_day(index: usize) -> &'static str {
if index < DAYS.len() {
DAYS[index]
} else {
"Неизвестный день"
}
}
fn main() {
for i in 0..7 {
println!("День {}: {}", i + 1, get_day(i));
}
// Массив можно использовать напрямую
println!("Первый день: {}", DAYS[0]);
}
Важное замечание про static mut
Если вам нужен изменяемый глобальный массив, используйте static mut с unsafe-блоком, но это крайне не рекомендуется. Лучше использовать Mutex или RwLock для потокобезопасности:
use std::sync::Mutex;
static DAYS_MUT: Mutex<[&'static str; 7]> = Mutex::new(["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]);
fn main() {
let mut days = DAYS_MUT.lock().unwrap();
days[0] = "Понедельник";
println!("{:?}", *days);
}
Таким образом, для большинства случаев используйте static с &'static str — это самый простой и безопасный способ.
Альтернативный подход: const с массивом строк через String
Если вам принципиально использовать const, можно сохранить массив владеющих строк String, но это потребует lazy_static или once_cell, так как String — это не константный тип. Вот пример с once_cell:
use once_cell::sync::Lazy;
static DAYS: Lazy<[String; 7]> = Lazy::new(|| {
[
"Пн".to_string(),
"Вт".to_string(),
"Ср".to_string(),
"Чт".to_string(),
"Пт".to_string(),
"Сб".to_string(),
"Вс".to_string(),
]
});
fn main() {
println!("{}", DAYS[0]);
}
Когда это полезно?
- Если массив нужно модифицировать (хотя
constне позволяет изменений, ноLazyможет быть использован сMutex). - Если строки генерируются динамически (например, из внешнего источника).
Минусы
- Требуется внешняя зависимость (
once_cellилиlazy_static). - Инициализация происходит при первом обращении, а не на этапе компиляции.
Для простых константных массивов строк используйте static с &'static str — это идиоматично и не требует дополнительных зависимостей.