Я делаю что-то с MaybeUninit
и FFI в Rust, что, кажется, работает, но я подозреваю, что это может быть неправильно или полагаться на неопределенное поведение.
Моя цель - иметь структуру MoreA
extension структура A
, включая A
в качестве начального поля. А затем вызвать некоторый код C, который пишет в структуру A
. А затем завершите MoreA
, заполнив его дополнительные поля, основываясь на том, что в A
.
В моем приложении все дополнительные поля MoreA
являются целыми числами, поэтому мне не о чем беспокоиться о назначениях для них, отбрасывая (неинициализированные) предыдущие значения.
Вот минимальный пример:
use core::fmt::Debug;
use std::mem::MaybeUninit;
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(C)]
struct A(i32, i32);
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(C)]
struct MoreA {
head: A,
more: i32,
}
unsafe fn mock_ffi(p: *mut A) {
// write doesn't drop previous (uninitialized) occupant of p
p.write(A(1, 2));
}
fn main() {
let mut b = MaybeUninit::<MoreA>::uninit();
unsafe { mock_ffi(b.as_mut_ptr().cast()); }
let b = unsafe {
let mut b = b.assume_init();
b.more = 3;
b
};
assert_eq!(&b, &MoreA { head: A(1, 2), more: 3 });
}
Звучит ли код let b = unsafe { ... }
? Он работает нормально и Мири не жалуется .
Но MaybeUninit
документы говорят:
Более того, неинициализированная память Особенность в том, что компилятор знает, что он не имеет фиксированного значения. Это делает неопределенным поведение иметь неинициализированные данные в переменной, даже если эта переменная имеет целочисленный тип, который в противном случае может содержать любой фиксированный битовый шаблон.
Кроме того, в книге Rust говорится, что Поведение считается неопределенным включает в себя:
Создание недопустимого значения, даже в частных полях и локальных. «Создание» значения происходит всякий раз, когда значение присваивается или читается из места, передается функции / примитивной операции или возвращается из функции / примитивной операции. Следующие значения недопустимы (в соответствующем типе):
... Целое число (i * / u *) или ..., полученное из неинициализированной памяти.
С другой стороны, представляется невозможным записать в поле more
1047 * до вызова assume_init
. Позже на той же странице:
В настоящее время не поддерживается способ создания необработанного указателя или ссылки на поле структуры внутри MaybeUninit. Это означает, что невозможно создать структуру, вызвав MaybeUninit :: uninit: :() и затем записав его в поля.
Если то, что я делаю в приведенном выше примере кода вызывает неопределенное поведение, какими будут решения?
Я бы хотел не включать в бокс значение A (то есть, я бы хотел, чтобы оно было непосредственно включено в MoreA
).
Я бы также хотел избежать создания одного A
для передачи на mock_ffi
, а затем копирования результатов в MoreA
. A
в моем реальном приложении - это большая структура.
Я думаю, если бы не было никакого здравого способа получить то, чего я добиваюсь, я бы выбрал один из них. два запасных варианта.
Если структура A относится к типу, который может содержать битовую комбинацию 0 в качестве допустимого значения, то я думаю, что третий запасной вариант будет:
Начните с
MaybeUninit::zeroed()
вместо
MaybeUninit::uninit()
.