Как объявлять / управлять константами с помощью нетривиальных инициализаторов

#rust

#Ржавчина

Вопрос:

Я хотел бы инициализировать некоторые константы в самом верху моего main.rs файла, например:

 const PRIVATE_KEY: Vec<u8> = std::fs::read("./jwtRS256.key").unwrap();
const PUBLIC_KEY: Vec<u8> = std::fs::read("./jwtRS256.pub.key").unwrap();

fn main () {
  // do some stuff
}
  

Однако я получаю ошибку компилятора следующим образом:

 error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
  --> src/main.rs:16:30
   |
16 | const PRIVATE_KEY: Vec<u8> = std::fs::read("./jwtRS256.key").unwrap();
   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  

Я понимаю; имеет смысл. Компилятор хочет иметь возможность оценивать значения этих констант во время компиляции, но не может. Однако, какова альтернатива? Мне нужны эти константы, доступные во всем моем коде, в том числе в других модулях. Итак, я не могу просто переместить их как обычные переменные в main функцию. Что мне делать вместо этого? (Я на Rust 1.47.)

Ответ №1:

Есть две возможности, и немного неясно, какую из них вы хотите:

Встроить содержимое файлов в двоичный файл

include_bytes и include_str будут считывать файлы во время компиляции и вставлять их содержимое (в соответствии с машиной, скомпилировавшей двоичный файл) в финальную программу.

Время выполнения глобальное

Если вы хотите читать файлы при запуске программы и делать их содержимое доступным глобально, вы хотите lazy_static или once_cell вместо этого они позволяют запущенному коду инициализировать глобальный во время выполнения.

const v static

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

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

Часто это то, что вам нужно, например, для простых чисел (таким образом, они компилируются как немедленные, а не загружаются из памяти), но для более сложных типов или больших объемов данных static , как правило, лучше выбирать.

Комментарии:

1. Пример OP считывает файлы во время выполнения (если это сработало).

2. Это немного неясно, поэтому я добавлю аннотацию о lazy_static / once_cell .

3. Кроме того, неверно, что const это «расширено».

4. @SvenMarnach Это потому, что целочисленные константы быстрее, когда они встроены, но компилятор не обязан их вставлять. В ответе утверждается, что это не так, что неверно, потому что в реализации нет такого требования.

5. @Masklinn Нет, в официальной ссылке этого не сказано. В нем говорится «по существу встроено», чтобы объяснить семантику, а не реализацию. Уточняется, что адреса могут не совпадать (но могут быть), и они могут быть или не быть стабильными. Короче говоря, это дает свободу реализации делать все, что лучше. Строковые литералы могут (и во многих случаях) быть встроены в код, если они достаточно малы и их адрес не берется. Никто не говорит, что строковые литералы гарантированно являются общими или не являются общими. Однако утверждение, что они никогда не используются совместно, неверно.

Ответ №2:

Если эти файлы присутствуют и находятся во время компиляции, и ваша единственная цель — включить их в компиляцию, тогда вы можете просто использовать include_bytes!("./jwtRS256.key") . Однако, если вы хотите читать эти файлы во время выполнения, рассмотрите возможность использования lazy_static:

 lazy_static! {
    pub static ref PRIVATE_KEY: Vec<u8> = std::fs::read("./jwtRS256.key").unwrap();
}
  

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