#rust #constants #inline
#Ржавчина #константы #встроенное
Вопрос:
Константы сохраняются в течение всего срока службы программы. Более конкретно, константы в Rust не имеют фиксированного адреса в памяти. Это потому, что они эффективно встроены в каждое место, в котором они используются. По этой причине ссылки на одну и ту же константу не обязательно гарантированно ссылаются на один и тот же адрес памяти.
Я видел только «встроенные функции» в C , но никогда не встроенные постоянные значения. Каково понятное для начинающих объяснение того, как это работает?
Меня также смущает «отсутствие фиксированного адреса в памяти». Означает ли это, что каждый раз, когда мы используем const
значение, значение в стеке выделяется только для этого выражения, и после завершения выполнения выражения оно будет уничтожено?
Ответ №1:
Я видел только «встроенные функции» в C , но никогда не встроенные постоянные значения.
Ближайшим приближением к a const
в Rust является enum
в C .
Каково понятное для начинающих объяснение того, как это работает?
Простое объяснение для начинающих таково: это просто работает, не беспокойтесь о мельчайших деталях.
Меня также смущает «отсутствие фиксированного адреса в памяти». Означает ли это, что каждый раз, когда мы используем
const
значение, значение в стеке выделяется только для этого выражения, и после завершения выполнения выражения оно будет уничтожено?
ДА. Возможно. Нет.
Это означает именно то, что написано на tin: гарантия не предоставляется. Это оставляет компилятору максимальную свободу для оптимизации.
Хорошо, это все хорошо, но… что происходит на самом деле?
На практике существуют две ситуации:
- значение достаточно простое: оно даже не касается стека, а вместо этого жестко закодировано непосредственно в сборке. Это, скорее всего, произойдет, например, для целых чисел.
- значение не так просто: оно создается в памяти, доступной только для чтения, и ссылается / копируется оттуда. Несколько копий в стеке будут иметь разные адреса.
Что означает simple? Ну, это зависит. Для каждого сайта вызова компилятор может решить «достаточно просто» или нет, где оно близко к встраиванию.
Означает ли это, что каждый раз, когда мы используем
const
значение, значение в стеке выделяется только для этого выражения, и после завершения выполнения выражения оно будет уничтожено?
Оно не будет уничтожено. const
переменные не могут иметь тип, который реализует Drop
. Значение просто забывается, когда оно больше не используется. Если оно когда-либо занимало память в стеке, эта память будет перезаписана через некоторое время.
Комментарии:
1. Можете ли вы объяснить, почему C enum является наиболее близким?
2. @PavelStrakhov:
enum
значение в C — это именованное значение без адреса памяти (если у вас есть,enum { X };
тогдаamp;X
возникнет ошибка времени компиляции), а значение перечисления встроено в каждый сайт использования. Это немного ближе, чем#define
потому что оно вычисляется только один раз (и это также намного чище: учитывает области видимости и т.д. ).3. Вы говорите так, как будто компилятор может решить преобразовать его в
static
, чтобы избежать копий, но так ли это на самом деле? Кажется, я не могу заставить компилятор сделать это; он всегда выдает код, эквивалентный встраиванию выражения, даже если оно явно неоптимально.4. @Veedrac: Я думал, что rustc в этом отношении ведет себя как Clang, но мои воспоминания могут сыграть со мной злую шутку, тем более, что я слишком много думал о том,
const fn
и какие значения могут в конечном итоге храниться вconst
(например,String
,HashMap
но неArc
илиRefCell
, потому что внутренняя изменчивость предотвращает хранение в ПЗУ). Я полагаю, что это еще один пример того, как работа переносится в LLVM:x5. @Veedrac: Я проверил несколько вариантов на игровой площадке, и действительно, rustc выдает определение, встроенное в IR. Конечно, в Release LLVM просто постоянно сворачивает его, и вы получаете целое число непосредственно в регистре.
Ответ №2:
const N: i32 = 5
в Rust это похоже #define N 5
на C или C , выполненное с безопасностью типов.
Вы можете думать об этом как о текстовой замене, когда тип совпадает, то есть let foo = 32 N;
эквивалентен let foo = 32 5;
в вашем примере.
Комментарии:
1. Я бы предпочел сравнить его с
enum
, чем с#define
, посколькуenum
у него (немного) больше безопасности типов.
Ответ №3:
const
Ant вообще не ведет себя как обычная переменная; когда вы определяете ее, она даже не получает свою собственную область видимости для целей проверки заимствования.
Сравните MIR, сгенерированный следующими фрагментами кода:
fn main() {
const N: i32 = 5;
}
и
fn main() {
let n: i32 = 5;
}
И вы обнаружите, что то, что N
расширяется, больше похоже на функцию, чем на переменную:
const main::N: i32 = {
let mut _0: i32; // return pointer
bb0: {
_0 = const 5i32; // scope 0 at <anon>:2:20: 2:21
return; // scope 0 at <anon>:2:5: 2:22
}
}
Когда оно используется в выражении, его значение помещается в стек для целей этого выражения и сразу после этого забывается.
Редактировать: по крайней мере, это то, что происходит на уровне MIR. Я не специалист по низкоуровневой оптимизации, поэтому фактический результат (действительно ли использовалась память стека) должен быть проверен в LLVM или даже ASM. Обратите внимание, что это также применимо к обычным переменным.