номикон дает интересный способ отображения того, как структура в конечном итоге будет размещена в памяти. Там же сказано, что для представления по умолчанию никаких гарантий не дается. Хотя я могу проверить выравнивание и размер с помощью std::mem::align_of
и std::mem::size_of
, есть ли способ получить точный способ, которым Rust изложил мою структуру/перечисление, например. таблица с именами полей и смещениями?
Предпочтительно во время выполнения, но я просто подумал, что мне может потребоваться копаться в сгенерированной сборке.
Вы не можете использовать информацию во время выполнения (она стирается), но вы можете заставить компилятор распечатать ее для вас во время сборки.
Это достаточно хорошо, как мне это сделать?
Вы можете использовать флаг компилятора --print-type-sizes
(требуется nightly). Если вы используете Cargo, используйте cargo rustc
(обратите внимание, что для этого требуется cargo clean
, спасибо @tifrel):
cargo +nightly rustc -- -Zprint-type-sizes
Например.:
struct Foo(i16, i32);
fn main() { _ = Foo(0, 0); }
Выход (rustc +nightly -Zprint-type-sizes file.rs
):
print-type-size type: `Foo`: 8 bytes, alignment: 4 bytes
print-type-size field `.1`: 4 bytes
print-type-size field `.0`: 2 bytes
print-type-size end padding: 2 bytes
print-type-size type: `[closure@std::rt::lang_start<()>::{closure#0}]`: 8 bytes, alignment: 8 bytes
print-type-size end padding: 8 bytes
print-type-size type: `std::result::Result<isize, !>`: 8 bytes, alignment: 8 bytes
print-type-size variant `Ok`: 8 bytes
print-type-size field `.0`: 8 bytes
print-type-size type: `std::process::ExitCode`: 4 bytes, alignment: 4 bytes
print-type-size field `.0`: 4 bytes
print-type-size type: `std::sys::windows::process::ExitCode`: 4 bytes, alignment: 4 bytes
print-type-size field `.0`: 4 bytes
Есть некоторые скрытые типы из main()
и наш тип. Мы видим, что это 8 байтов с выравниванием по 4 байтам (из-за i32
). Мы также можем видеть, что компилятор переупорядочил поля так, чтобы i16
стоял последним, а затем 2 байта заполнения. Обратите внимание, что он печатает только используемые типы (поэтому мы использовали его в main()
) и печатает мономорфные типы (после применения дженериков).
Другой способ вывести более подробную и конкретную информацию о типе — использовать атрибут perm-unstable rustc_layout
:
#![feature(rustc_attrs)]
#[rustc_layout(debug)]
struct Foo(i16, i32);
fn main() {}
error: layout_of(Foo) = Layout {
fields: Arbitrary {
offsets: [
Size {
raw: 4,
},
Size {
raw: 0,
},
],
memory_index: [
1,
0,
],
},
variants: Single {
index: 0,
},
abi: ScalarPair(
Scalar {
value: Int(
I32,
true,
),
valid_range: 0..=4294967295,
},
Scalar {
value: Int(
I16,
true,
),
valid_range: 0..=65535,
},
),
largest_niche: None,
align: AbiAndPrefAlign {
abi: Align {
pow2: 2,
},
pref: Align {
pow2: 3,
},
},
size: Size {
raw: 8,
},
}
--> rs.rs:3:1
|
3 | struct Foo(i16, i32);
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
Однако обратите внимание, что это печатает немономорфные типы, поэтому struct Foo<T>(T)
будет печатать layout error: Unknown(T)
. Из-за этого он также не требует использования типа.
Я не могу отредактировать ваш ответ, но я думаю, вы должны предупредить, что использование cargo +nightly rust -- ...
потребует предыдущего cargo clean
или не выдаст информацию.
Да, хороший момент. Будет редактировать.
Помимо благодарности за отличный ответ, означает ли perm-unstable просто то, что он нестабилен, и команда Rust решила никогда его не стабилизировать?
Да, потому что он используется в основном для отладки самого компилятора и может быть полезен разработчикам, отлаживающим свои приложения, но это не то, что вам следует использовать в рабочей среде.
«и может быть полезно для разработчиков, отлаживающих свои приложения, но это не то, что вы должны использовать в производстве», даже тогда это кажется действительно изворотливым, если вы не пытаетесь выделить поля, которыми манипулируют, из списков сборок.
Вы имеете в виду во время выполнения или как пользователь, который вызывает компилятор (для печати в терминале)?