gpt4 book ai didi

rust - 写入 MaybeUninit 结构中的字段?

转载 作者:行者123 更新时间:2023-12-03 11:24:16 25 4
gpt4 key购买 nike

我正在与 MaybeUninit 做一些事情和 Rust 中的 FFI 似乎有效,但我怀疑可能不健全/依赖于未定义的行为。

我的目标是拥有一个结构 MoreA扩展结构 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 { ... }声音?它运行正常, Miri doesn't complain .

但是 the MaybeUninit docs说:

Moreover, uninitialized memory is special in that the compiler knows that it does not have a fixed value. This makes it undefined behavior to have uninitialized data in a variable even if that variable has an integer type, which otherwise can hold any fixed bit pattern.



另外,Rust 书说 Behavior considered undefined包括:

  • Producing an invalid value, even in private fields and locals. "Producing" a value happens any time a value is assigned to or read from a place, passed to a function/primitive operation or returned from a function/primitive operation. The following values are invalid (at their respective type):

    ... An integer (i*/u*) or ... obtained from uninitialized memory.



另一方面,似乎不可能写入 more调用前的字段 assume_init .稍后在同一页面上:

There is currently no supported way to create a raw pointer or reference to a field of a struct inside MaybeUninit. That means it is not possible to create a struct by calling MaybeUninit::uninit::() and then writing to its fields.



如果我在上面的代码示例中所做的确实触发了未定义的行为,那么解决方案是什么?
  • 我想避免装箱 A 值(也就是说,我想将它直接包含在 MoreA 中)。
  • 我也希望避免创建一个 A转至 mock_ffi然后必须将结果复制到 MoreA . A在我的实际应用中是一个大型结构。

  • 我想如果没有可靠的方法来获得我所追求的东西,我将不得不选择这两个后备之一。

    如果 struct A 是一种可以将位模式 0 保存为有效值的类型,那么我猜第三个回退是:
  • MaybeUninit::zeroed() 开始而不是 MaybeUninit::uninit() .
  • 最佳答案

    目前,引用任何类型的未初始化内存的唯一合理方法是 MaybeUninit .实际上,读取或写入未初始化的整数可能是安全的,但这并没有正式记录。读取或写入未初始化的 bool 绝对不安全或大多数其他类型。

    通常,如文档所述,您不能逐个字段初始化结构字段。但是,只要符合以下条件,这样做是合理的:

  • 该结构具有 repr(C) .这是必要的,因为它可以防止 Rust 做聪明的布局技巧,从而布局 MaybeUninit<T> 类型的字段。与 T 类型的字段的布局保持相同,无论其相邻字段如何。
  • 每个字段都是 MaybeUninit .这让我们assume_init()对于整个结构,然后单独初始化每个字段。

  • 鉴于您的结构已经是 repr(C) ,您可以使用使用 MaybeIninit 的中间表示对于每个领域。 repr(C)也意味着一旦初始化,我们就可以在类型之间进行转换,前提是两个结构具有相同顺序的相同字段。
    use std::mem::{self, MaybeUninit};

    #[repr(C)]
    struct MoreAConstruct {
    head: MaybeUninit<A>,
    more: MaybeUninit<i32>,
    }

    let b: MoreA = unsafe {
    // It's OK to assume a struct is initialized when all of its fields are MaybeUninit
    let mut b_construct = MaybeUninit::<MoreAConstruct>::uninit().assume_init();
    mock_ffi(b_construct.head.as_mut_ptr());
    b_construct.more = MaybeUninit::new(3);
    mem::transmute(b_construct)
    };

    关于rust - 写入 MaybeUninit 结构中的字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61318595/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com