如何在不安全代码中初始化枚举中的结构

问题描述 投票:0回答:1

我正在尝试使用 Box::new_uninit_in 和 ptr::addr_of_mut! 初始化一个枚举变体,在我的例子中它是一个结构体。然而,我正在努力进入正确的领域。有人可以帮我解决这个问题吗?

#![feature(allocator_api)]
use std::alloc::Allocator;
use std::mem::MaybeUninit;
use core::ptr::{self, NonNull};

fn main() {}

enum Node<K, V> {
    LeafNode {
        size: u16,
        keys: [MaybeUninit<K>; 10],
        vals: [MaybeUninit<V>; 10],
        prev: Option<NonNull<Node<K, V>>>,
        next: Option<NonNull<Node<K, V>>>,
    },
    InternalNode {
        size: u16,
        keys: [MaybeUninit<K>; 10],
        vals: [MaybeUninit<NonNull<Node<K, V>>>; 11],
    },
}

impl<K, V> Node<K, V> {
    unsafe fn init(this: *mut Self) {
        unsafe {
            // 1. How do I access the fields of the struct within the enum?
            // 2. How can I initialize the enum as either the LeafNode variant or the InternalNode variant?
            ptr::addr_of_mut!((*this).size).write(0);
        }
    } 
    fn new<A: Allocator + Clone>(alloc: A) -> Box<Self, A> {
        unsafe {
            let mut node = Box::new_uninit_in(alloc);
            Node::init(node.as_mut_ptr());
            node.assume_init()
        }
    }
}

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=a6f780637a195cce4a5c4aabff131bfc

我尝试使用

node
Node::LeafNode
转换为
node as Node::LeafNode
,但 Rust 不允许我这样做。也许我做错了。

pointers rust struct enums unsafe
1个回答
1
投票

初始化非常简单:

this.write(Self::LeafNode {
    size: 0,
    keys: MaybeUninit::uninit_array(),
    vals: MaybeUninit::uninit_array(),
    prev: None,
    next: None,
});
this.write(Self::InternalNode {
    size: 0,
    keys: MaybeUninit::uninit_array(),
    vals: MaybeUninit::uninit_array(),
});

对于访问变异,您将需要一个参考。您无法(在稳定版上)手动获取枚举内字段的偏移量。

不过,这并没有那么糟糕:如果您已经初始化了变体,通常可以创建对它的引用。如果您遇到锯齿问题,请考虑

UnsafeCell

如果您确实需要访问字段,您可以使用

#[repr(int)]
#[repr(C)]
枚举,它们的布局已定义,您可以计算它。

在夜间你可以做你想做的事:有

offset_of!()
(宏是稳定的,但它的部分行为仍然不稳定):

this.byte_add(std::mem::offset_of!(Self, LeafNode.size))
    .cast::<u16>()
    .write(0);
this.byte_add(std::mem::offset_of!(Self, LeafNode.prev))
    .cast::<Option<NonNull<Node<K, V>>>>()
    .write(None);
this.byte_add(std::mem::offset_of!(Self, LeafNode.next))
    .cast::<Option<NonNull<Node<K, V>>>>()
    .write(None);

在大多数情况下,这种方法相对于简单方法应该没有优势(当然,除了它更冗长并且让你看起来更聪明)。更糟糕的是,即使使用

offset_of!()
,您也无法初始化 判别式 ,因此您将永远无法创建对枚举的引用或对其进行模式匹配。

© www.soinside.com 2019 - 2024. All rights reserved.