Я пересматриваю старый код и теперь сталкиваюсь с ошибкой (синтетическое воссоздание):
error[E0793]: reference to packed field is unaligned
--> src/main.rs:9:22
|
9 | println!("{:?}", my_struct.field);
| ^^^^^^^^^^^^^^^
|
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
Как я могу это исправить?
NOTE: This is an attempt to create a canonical Q&A for users experiencing the titular error.
Все ссылки в Rust должны быть выровнены, но поля в #[repr(packed)]
могут не соответствовать собственному выравниванию. Предыдущие версии компилятора ошибочно допускали ссылки на поля с неправильным выравниванием, но теперь это ошибка. Документация E0793.
Если вам необходимо использовать упакованные поля, которые не полностью выровнены, к ним все равно можно получить доступ и назначить их обычным образом (например, my_struct.field
). Однако вы не можете использовать их в контексте, где создается ссылка. И простой способ избежать этой ситуации — сначала скопировать/переместить поле в другую переменную:
let field = my_struct.field;
println!("{:?}", field);
my_struct.field = ...;
// a block expression can also be used to create a temporary value instead of a place expression
println!("{:?}", { my_struct.field });
Если вам необходимо использовать поля в их невыровненном положении, вы должны получить к ним доступ через указатель, полученный с помощью addr_of! / addr_of_mut! и работать с ними через read_unaligned и write_unaligned. В связанной документации есть примеры специально для упакованных структур.
let field_ptr = std::ptr::addr_of!(my_struct.field);
let field = unsafe { field_ptr.read_unaligned() };
println!("{:?}", field);
Другой вариант, конечно, — выровнять поле, полностью удалив #[repr(packed)]
.
Если вы столкнулись с этой ошибкой при компиляции зависимости, вы не сможете исправить ее самостоятельно. В идеальном случае сопровождающие зависимости уже устранили проблему в новой версии. Итак, сначала попробуйте cargo update <DEPENDENCY>
. Если это не помогло и это прямая зависимость, имеющая более новую, несовместимую с Semver версию, попробуйте обновить ее вручную в своем Cargo.toml (или cargo upgrade -p <DEPENDENCY>
, если она у вас установлена). Если ошибка связана с вложенной зависимостью, попробуйте сделать то же самое с прямой промежуточной зависимостью. Используйте cargo tree -i <DEPENDENCY>
, чтобы увидеть, какая из ваших зависимостей зависит от сломанной.
Известные случаи:
ntapi
0.3: обновление до 0.4tendril
0.4.2: обновление до 0.4.3Если текущего исправления нет, вы можете разветвить зависимость и [исправить] в свой собственный пакет.
Или вы можете понизить версию Rust, чтобы обойти ошибку. Вот различия версий:
#[allow(unaligned_references)]
)Таким образом, использование 1.52.1 даже не будет предупреждать, 1.61.0 выдаст предупреждение, но разрешит его, а 1.68.2 разрешит это, если это настроено. Однако имейте в виду, что это всегда было неопределенное поведение, даже в старых версиях; код-нарушитель может вызывать или не вызывать ошибки в зависимости от вашей целевой системы.
Обратите внимание, что вам не обязательно нужен
read/write_unaligned()
и небезопасный код: возможно, вам удастся скопировать значение или напрямую присвоить его (для println!() используйте {value }; фигурные скобки копируют поле во временное поле).