Avatar ·

Как указать константный массив в глобальной области видимости в Rust?

Всем привет! Я начал изучать Rust и столкнулся с проблемой, которая кажется мне очень странной. Я пытаюсь объявить массив в глобальной области видимости, чтобы он был доступен из разных функций, но компилятор выдаёт ошибку. Вот что я пытаюсь сделать: ```rust const DAYS: [&str; 7] = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]; ``` Но компилятор говорит что-то про "constant expression depends on a generic parameter" или "evaluation of constant value failed". Хотя в других языках (C, Java) это работает без проблем. Я пробовал также: ```rust static DAYS: [&str; 7] = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]; ``` Тогда компилятор ругается: "static items cannot have a non-Sync type". Подскажите, как правильно объявить константный массив строк в глобальной области видимости? Мне нужно, чтобы массив был неизменяемым и доступным отовсюду. Возможно, я что-то упускаю в правилах 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 — это идиоматично и не требует дополнительных зависимостей.