Отказ от ответственности: этот ответ является обоснованным предположением.
TL; DR: я полагаю, что массивы явного заполнения и явная инициализация предназначены для того, чтобы избежать оставления любого неинициализированного байта, и неопределенное поведение, которое приводит к этому.
Обход через C
Важно понимать, что LLVM наследует большую часть своей низкоуровневой семантики от C , В конце концов, его первым и главным интерфейсом является Clang, и это во многом сформировало его.
Когда Clang понижает struct
до LLVM IR, он уверенно оставляет LLVM, чтобы выяснить отступы link .
Таким образом:
struct A
{
int a;
struct { char const* ptr; size_t len; } str;
char c;
};
A const GLOBAL{ 1, { "hello", 5 }, 'c' };
понижен до:
%struct.A = type { i32, %struct.anon, i8 }
%struct.anon = type { i8*, i64 }
@_ZL6GLOBAL = internal constant %struct.A
{
i32 1,
%struct.anon
{
i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str, i32 0, i32 0),
i64 5
},
i8 99
}, align 8
@.str = private unnamed_addr constant [6 x i8] c"hello\00", align 1
Это означает, что байты заполнения остаются неинициализированными, и в типичном C считывание моды неинициализированными байтами является неопределенным поведением.
Это означает, что битовое копирование структуры с неинициализированными байтами заполнения является неопределенным поведением, и в то время как вызовы memcpy
(пониженные до внутренних) кажутся незатронутыми, я я не знаю ни одного положения в стандарте C, которое дает memcpy
пропуск ...
Back to Rust
Rust занимает сильную позицию всякий раз, когда появляется неопределенное поведение :
- В безопасном Rust не должно быть неопределенного поведения 1 .
- В небезопасном ржавчине не должно быть неопределенного поведения.
Оставив позади неинициализированный па Добавление байтов и отключение пользователей, выполняющих битовое копирование, очень похоже на ненужный источник неопределенного поведения:
Кажется, что выигрыш в производительности невелик (если есть): Rust может свободно переставлять Члены структуры и структуры сжатия, как правило, содержат очень мало байтов заполнения (только несколько завершающих).
Поэтому я предполагаю, что rust c явно указывает массивы заполнения 2 и явно инициализирует их, чтобы избежать оставления любого неинициализированного байта заполнения.
1 Есть, все еще. Например, из-за того, что LLVM считает, что преобразование float
в int
является UB, если значение не подходит, или LLVM, учитывая, что бесконечное l oop без побочных эффектов является UB - оба унаследованы от C. Это незавершенная работа.
2 Это не дает обоснования для массивов нулевого размера, которые кажутся мне совершенно излишними.