将 "out slice" 传递给 C 函数的惯用方式?

Idiomatic way to pass "out slice" to C function?

Some Windows APIs以slice为参数,在return上写入。它类似于 out 指针,但采用切片形式(因此调用者不需要传递额外的“长度”参数)。

在 out 指针的情况下,我一直在使用 MaybeUninit,我认为这是 Rust 中惯用的方式。但是,我不知道如何在切片的情况下使用它。

例如,许多示例建议声明 [MaybeUninit<u16>; 32],但如何将其传递给仅接受 &mut [u16] 的函数?我尝试了 MaybeUninit<[u16; 32]>,但无法从 MaybeUninit 中获取未初始化的 &mut T。只有as_mut_ptr,是指针,不是slice。

我现在应该坚持 let x: [u16; 32] = zeroed(); 吗?

您不需要 MaybeUninit,您可以自己用零填充缓冲区数组:

let x: [u16; 32] = [0u16; 32];

let res = unsafe { GetClassNameW(param0, &mut x); }

创建对未初始化内存的引用是未定义行为,即使从未读取内存内容。引用 the reference, "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):
    • ...
    • A reference or Box<T> that is dangling, unaligned, or points to an invalid value.

...

Note: Uninitialized memory is also implicitly invalid for any type that has a restricted set of valid values. In other words, the only cases in which reading uninitialized memory is permitted are inside unions and in "padding" (the gaps between the fields/elements of a type).

这不是显而易见的这是UB:有an active discussion about that(部分反驳是允许像你的情况这样的事情)。但是,目前它被认为是 UB,你应该避免它。

(注意:“具有一组受限制的有效值”有点模棱两可。是否允许像整数这样的原始类型包含未初始化的位是also a matter of active discussion,但在这种情况下你也应该避免它直到它已经解决了。您可以声称参考文献不同意这一点,因为整数没有一组受限制的值,但这是错误的:可以将一个字节的一组可能值视为 0-256 and uninit 字节,实际上这是很多地方使用的解释。整数不能包含 uninit 字节,因此有限制一组值)。

使用 mem::zeroed() 初始化数组是合理的,但无缘无故地使用 unsafe:您可以只使用初始化的数组,即 [0; size],它的性能也一样。