如何在 Rust 中创建*通用*零大小类型实例?

Rob*_*min -1 rust

如果该类型是泛型类型参数,如何创建零大小类型的实例?

(!) 我对“The Rustonomicon”9.10 提供的答案不感兴趣。处理零大小类型、片段Some(ptr::read(NonNull::<T>::dangling().as_ptr()))

例如

fn get<T>() -> Option<T>
    if size_of::<T>() == 0 {  // If the type parameter T is a zero-sized type
        return Some< ??? >    // then return the instance of that type.
    // Otherwise return something different.
Run Code Online (Sandbox Code Playgroud)

???我可以在上面的代码中放置什么来代替?

理想情况下,我希望此代码适用于作为 传递的任何零大小类型T

  • 无论该类型是否实现,Default
  • 该类型是否是
    • 一个空结构体 ( struct Zst;, struct Zst{}),
    • 空元组 ( ())
    • 空或单值枚举 ( enum Zst;, enum Zst { TheOnlyValue })
    • 空数组( [u8; 0])
    • 任何其他零尺寸类型
    • 上面列出的组合(结构/数组/元组等)

最终我需要以下内容:

impl<T> Iterator for RawIter<T> {   // Some generic code.
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        if size_of::<T>() == 0 {  // If the type parameter T is a zero-sized type
            Some< ??? >           // then return the instance of that type.
Run Code Online (Sandbox Code Playgroud)

更正

  • 2023.12.05 19:00 PST:(
    !)我对“The Rustonomicon”9.10 提供的答案不感兴趣。处理零大小类型因为片段Some(ptr::read(NonNull::<T>::dangling().as_ptr()))(和*std::ptr::NonNull::<T>::dangling().as_ptr())似乎以两种方式导致未定义的行为,请参阅此问题项 3.B (dangling ptr deref) 和 4 (uninit mem read)
    仅供参考:@kmdreko,@chayim-friedman。
    似乎也std::mem::MaybeUninit::<T>::uninit().assume_init()有导致未定义行为的风险(uninit mem read),请参阅Should_init() 安全性此问题第 4 项
    。仅供参考:@chayim-friedman、@ballpointben。
    在剩下的 2 个 @chayim-friedman 的项目中,似乎std::mem::zeroed::<T>()获胜,因为它比它的同义词短std::mem::MaybeUninit::<T>::zeroed().assume_init()

Cha*_*man 5

构建 ZST 有多种可能的方法:

  • std::mem::zeroed::<T>()
  • std::mem::MaybeUninit::<T>::uninit().assume_init()
  • std::mem::MaybeUninit::<T>::zeroed().assume_init()
  • *std::ptr::NonNull::<T>::dangling().as_ptr()

ETC..

还有一个已接受的 API 更改提案,该conjure_zst()提案将成为构造 ZST 的方式,但在撰写本文时尚未实现。

但是,请注意,构造任意 ZST(或任何类型)是不合理的,因为类型可以携带不变量。例如,下面的代码是完全正确的:

pub struct ImpossibleToConstruct(());

impl ImpossibleToConstruct {
    pub fn cause_ub(self) {
        unsafe { std::hint::unreachable_unchecked() };
    }
}
Run Code Online (Sandbox Code Playgroud)

但如果你提供一个get()函数来构造任意 ZST,用户就可以在安全代码中引发 UB:

get::<ImpossibleToConstruct>().cause_ub();
Run Code Online (Sandbox Code Playgroud)

诚然,这个例子相当愚蠢,但是带有不变量的 ZST 有很多用例。

因此,如果你想提供这样的功能,它必须是unsafe(上面提到的将来应该填补的角色conjure_zst())或受某些约束unsafe trait SafeToConjureZst(你可以提供一个#[derive]for,以使用户代码完全安全,但在这一点上你最好使用bytemuck::Podwithbytemuck::cast::<(), T>(())来安全地完成这一切)。