Как обработать ошибку «ссылка на упакованное поле не выровнена»?

Я пересматриваю старый код и теперь сталкиваюсь с ошибкой (синтетическое воссоздание):

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.

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
4
0
288
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Все ссылки в 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.4
  • tendril 0.4.2: обновление до 0.4.3
  • использование макросов в упакованных структурах (serde-derive, pin-project и т. д.) может поддерживаться, а может и не поддерживаться.

Если текущего исправления нет, вы можете разветвить зависимость и [исправить] в свой собственный пакет.

Или вы можете понизить версию Rust, чтобы обойти ошибку. Вот различия версий:

  • В версии 1.53.0 это было предупреждением
  • В версии 1.62.0 эта ошибка по умолчанию (но ее можно разрешить с помощью #[allow(unaligned_references)])
  • 1.69.0 всегда делал эту ошибку

Таким образом, использование 1.52.1 даже не будет предупреждать, 1.61.0 выдаст предупреждение, но разрешит его, а 1.68.2 разрешит это, если это настроено. Однако имейте в виду, что это всегда было неопределенное поведение, даже в старых версиях; код-нарушитель может вызывать или не вызывать ошибки в зависимости от вашей целевой системы.

Обратите внимание, что вам не обязательно нужен read/write_unaligned() и небезопасный код: возможно, вам удастся скопировать значение или напрямую присвоить его (для println!() используйте {value }; фигурные скобки копируют поле во временное поле).

Chayim Friedman 09.05.2024 19:54

Другие вопросы по теме