我正在try 创建一个包含常量数组的容器对象, for each 元素使用一个常量初始值设定项函数.

对于固定大小的数组(用整数表示),它是already solved;这里的问题是数组的长度是常量泛型.

另一个转折是,我需要no_std(核心)模式下的整个东西.

下面是一个代码示例,它演示了我的问题:

// Important: Element is **not** `Copy`.
#[derive(Debug)]
struct Element(usize);
impl Element {
    pub const fn array_initializer(pos: usize) -> Self {
        Element(pos)
    }
}

#[derive(Debug)]
pub struct Container<const N: usize> {
    data: [Element; N],
}

impl<const N: usize> Container<N> {
    pub const fn new() -> Self {
        // The content of this function is the only
        // part that is open for change!

        // Task is: create a container with `N` elements,
        // where every element is initialized with
        // `Element::array_initializer`.
    }
}

static STATIC_CONTAINER: Container<5> = Container::new();

fn main() {
    println!("{:?}", STATIC_CONTAINER);
}

请注意,情况并非如此.

Container { data: [Element(0), Element(1), Element(2), Element(3), Element(4)] }

当然,在拉斯特目前的状态下,这也可能是完全不可能的;尽管我会对此感到非常难过.

我不在乎在Container::new函数中使用unsafe,只要它是sound即可.


到目前为止我try 过的东西:

  • array-const-fn-init

    它失败是因为它不接受常量泛型数组长度.

    impl<const N: usize> Container<N> {
        pub const fn new() -> Self {
            const fn element_init(pos: usize) -> Element {
                Element::array_initializer(pos)
            }
    
            let data: [Element; N] = array_const_fn_init::array_const_fn_init!(element_init; N);
    
            Self { data }
        }
    }
    
    error: proc macro panicked
      --> src/main.rs:21:34
       |
    21 |         let data: [Element; N] = array_const_fn_init::array_const_fn_init!(element_init; N);
       |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       |
       = help: message: Expected <usize>, found N
    
  • MaybeUninit,以this example为基础

    impl<const N: usize> Container<N> {
        pub const fn new() -> Self {
            use core::mem::MaybeUninit;
    
            let mut data: [MaybeUninit<Element>; N] = unsafe { MaybeUninit::uninit().assume_init() };
    
            {
                // const for's are not stabilized yet, so use a loop
                let mut i = 0;
                while i < N {
                    data[i] = MaybeUninit::new(Element::array_initializer(i));
                    i += 1;
                }
            }
    
            let data: [Element; N] =
                unsafe { core::mem::transmute::<[MaybeUninit<Element>; N], [Element; N]>(data) };
    
            Self { data }
        }
    }
    
    error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
      --> src/main.rs:30:22
       |
    37 |             unsafe { core::mem::transmute::<[MaybeUninit<Element>; N], [Element; N]>(data) };
       |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       |
       = note: source type: `[MaybeUninit<Element>; N]` (this type does not have a fixed size)
       = note: target type: `[Element; N]` (this type does not have a fixed size)
    

    这在技术上是无稽之谈,可能是编译器的限制,将来会取消;[MaybeUninit<Element>; N][Element; N]的大小肯定是一样的.

  • core::intrinsics::transmute_unchecked

    因为我们知道大小是一样的,无论如何用transmute_unchecked来做变形.

    现在我们在nightlyfeature的区域,遗憾的是这与我的情况不相容:/

    #![feature(core_intrinsics)]
    
    // ...
    
    impl<const N: usize> Container<N> {
        pub const fn new() -> Self {
            use core::mem::MaybeUninit;
    
            let mut data: [MaybeUninit<Element>; N] = unsafe { MaybeUninit::uninit().assume_init() };
    
            {
                // const for's are not stabilized yet, so use a loop
                let mut i = 0;
                while i < N {
                    data[i] = MaybeUninit::new(Element::array_initializer(i));
                    i += 1;
                }
            }
    
            let data: [Element; N] = unsafe {
                core::intrinsics::transmute_unchecked::<[MaybeUninit<Element>; N], [Element; N]>(data)
            };
    
            Self { data }
        }
    }
    

    这确实给了我们正确的结果,但feature(core_intrinsics)可能永远不会被包括在stable中.

  • MaybeUninit::array_assume_init

    这看起来确实有点理智,更接近稳定,但目前它仍然需要nightly个编译器和feature个门.

    #![feature(maybe_uninit_array_assume_init)]
    #![feature(const_maybe_uninit_array_assume_init)]
    
    // ...
    
    impl<const N: usize> Container<N> {
        pub const fn new() -> Self {
            use core::mem::MaybeUninit;
    
            let mut data: [MaybeUninit<Element>; N] = unsafe { MaybeUninit::uninit().assume_init() };
    
            {
                // const for's are not stabilized yet, so use a loop
                let mut i = 0;
                while i < N {
                    data[i] = MaybeUninit::new(Element::array_initializer(i));
                    i += 1;
                }
            }
    
            let data: [Element; N] = unsafe { MaybeUninit::array_assume_init(data) };
    
            Self { data }
        }
    }
    

推荐答案

理想的解决方案是:

因此,目前看来,以union为基数的变形是我能找到的最明智的 Select :

// Important: Element is **not** `Copy`.
#[derive(Debug)]
struct Element(usize);
impl Element {
    pub const fn array_initializer(pos: usize) -> Self {
        Element(pos)
    }
}

#[derive(Debug)]
pub struct Container<const N: usize> {
    data: [Element; N],
}
impl<const N: usize> Container<N> {
    pub const fn new() -> Self {
        use core::mem::{ManuallyDrop, MaybeUninit};

        let mut data: [MaybeUninit<Element>; N] = unsafe { MaybeUninit::uninit().assume_init() };

        {
            // const for's are not stabilized yet, so use a loop
            let mut i = 0;
            while i < N {
                data[i] = MaybeUninit::new(Element::array_initializer(i));
                i += 1;
            }
        }

        #[repr(C)]
        union InitializedData<const N: usize> {
            uninit: ManuallyDrop<[MaybeUninit<Element>; N]>,
            init: ManuallyDrop<[Element; N]>,
        }
        let data = ManuallyDrop::into_inner(unsafe {
            InitializedData {
                uninit: ManuallyDrop::new(data),
            }
            .init
        });

        Self { data }
    }
}

static STATIC_CONTAINER: Container<5> = Container::new();

fn main() {
    println!("{:?}", STATIC_CONTAINER);
}
Container { data: [Element(0), Element(1), Element(2), Element(3), Element(4)] }

这里很重要的一点是,Element::array_initializer不能惊慌失措,否则可能会有关于Drop行为的额外考虑.

Rust相关问答推荐

无法在线程之间安全地发送future (&Q;)&错误

rust 迹-内存管理-POP所有权-链表

MPSC频道在接收器处阻塞

如何导入crate-type=[";cdylib;]库?

交换引用时的生命周期

如何go 除多余的(0..)在迭代中,当它不被使用时?

如何在嵌套的泛型 struct 中调用泛型方法?

使用关联类型重写时特征的实现冲突

为什么AsyncRead在Box上的实现有一个Unpin特征绑定?

为什么特征默认没有调整大小?

从 rust 函数返回 &HashMap

Rust 编译器不统一在 if let 表达式的分支中都 impl Future 的类型

在 Rust 中使用 `SecTrustSettingsSetTrustSettings` 绑定导致 `errSecInternalComponent`

从 HashMap>, _> 中删除的生命周期问题

Rust 生命周期:这两种类型声明为不同的生命周期

在每个循环迭代中删除borrow

n 个范围的笛卡尔积

第 7.4 章片段中如何定义 `thread_rng`

为什么我可以从读取的可变自引用中移出?

返回 &str 但不是 String 时,borrow 时间比预期长