如何删除部分初始化的向量或数组的 MaybeUninit ?

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

我正在寻找使用的信息和良好实践

MaybeUninit
直接初始化集合(通常是数组或向量)和 如果初始化失败,请正确删除它们。

感谢 API 示例,我能够相当快地掌握 数组,但向量则要棘手得多。就这个例子来说 如下(这是我在项目中所做的简单简化), 泛型函数

try_new<T: TryFrom<()>, A:ArrayUninit<T>>(len: usize)

,尝试通过以下方式创建对象 
T
 的数组或向量
一个易出错的数据生成器
TryFrom::try_from(_:())
T
。数组生成的顺序是随机的
(异步);这是通过函数 
indices(len:usize)
 模拟的。
函数,
try_new<A:ArrayUninit>(len: usize)
,使用方法
ArrayUninit::try_uninit(len: usize)
,由 
Vec<Data>
[Data;N]
,用于构建未初始化的数组或向量。

在我们的

main

 中,我们使用数据类型 
Data
 作为示例,其中
生成器,
TryFrom<()>
已实现。

以下代码似乎有效,但我想知道 如何删除未初始化的数据:(

playground)

use core::{ time::Duration, mem::MaybeUninit, }; use std::thread; use rand::prelude::*; // trait with method for building uninited array/vector // implementations for Vec<T> and [T;N] after the main() trait ArrayUninit<T>: AsMut<[T]> + Sized { fn try_uninit(len: usize) -> Result<MaybeUninit<Self>,String>; } // generate shuffled indices fn indices(len: usize) -> Box<dyn Iterator<Item = usize>> { let mut vec: Vec<usize> = (0..len).collect(); vec.shuffle(&mut thread_rng()); Box::new(vec.into_iter()) } // try to build an array or a vector of objects T fn try_new<T: TryFrom<()>, A:ArrayUninit<T>>(len: usize) -> Result<A,String> { // build uninitialized collection let mut uninited = A::try_uninit(len)?; // simulate initialization in random order let indices = indices(len); // build a mutable ref to the array/vector let ra: &mut A = unsafe {(uninited.as_mut_ptr() as *mut A).as_mut() }.unwrap(); let mut failed = false; for i in indices { // get ptr at i let ptr_arr: * mut T = unsafe{AsMut::<[T]>::as_mut(ra).as_mut_ptr().add(i)}; // get object and break if failed let data = match T::try_from(()) { Ok(data) => data, Err(_) => { failed = true; break; }, }; // set object unsafe { *ptr_arr = data }; } if !failed { Ok(unsafe{ uninited.assume_init() }) // return array, if successful } else { // if failed, then for i in 0..len { // drop all objects within array/vector let ptr_arr: * mut T = unsafe{AsMut::<[T]>::as_mut(ra).as_mut_ptr().add(i)}; drop(unsafe { ptr_arr.read() }); } drop(uninited); // and drop uninited array/vector Err(format!("failed to init")) } } // Object Data #[derive(Debug)] struct Data(f64); impl TryFrom<()> for Data { type Error = (); // generate a float with errors; time consuming fn try_from(_:()) -> Result<Self,()> { thread::sleep(Duration::from_millis(10)); let f = rand::random(); if f <= 0.99 { Ok(Data(f)) } else { Err(()) } } } fn main() { let result: Result<Vec<Data>,_> = try_new(3); println!("result: {:?}",result); let result: Result<[Data;3],_> = try_new(3); println!("result: {:?}",result); let result: Result<Vec<Data>,_> = try_new(1000); println!("result: {:?}",result); let result: Result<[Data;1000],_> = try_new(1000); println!("result: {:?}",result); } impl<T> ArrayUninit<T> for Vec<T> { fn try_uninit(len: usize) -> Result<MaybeUninit<Self>,String> { let mut v: MaybeUninit<Vec<T>> = MaybeUninit::uninit(); let mut vv = Vec::with_capacity(len); unsafe { vv.set_len(len) }; v.write(vv); Ok(v) } } impl<T,const N: usize> ArrayUninit<T> for [T;N] { fn try_uninit(len: usize) -> Result<MaybeUninit<Self>,String> { if len == N { Ok(MaybeUninit::uninit()) } else { Err(format!("len differs from array size")) } } }
这是一个运行示例(结果是随机的):

Standard Error Compiling playground v0.0.1 (/playground) Finished dev [unoptimized + debuginfo] target(s) in 0.84s Running `target/debug/playground` Standard Output result: Ok([Data(0.9778296353515407), Data(0.9319034033060891), Data(0.11046580243682291)]) result: Ok([Data(0.749182522350767), Data(0.5432451150541627), Data(0.6840763419767837)]) result: Err("failed to init") result: Err("failed to init")
目前,为了防止失败,我删除了
数组/向量,已初始化和未初始化,然后我删除
数组/向量。这似乎有效,但令我惊讶的是,人们也可以
删除未初始化的数据。

任何人都可以确认这是否是放弃的正确方法 未初始化的数据?如果没有,要遵循什么规则?

[编辑]: 感谢@isaactfa和@Chayim的评论,我更新了代码如下(
playground):

use core::{ time::Duration, mem::MaybeUninit, }; use std::thread; use rand::prelude::*; // trait with method for building uninited array/vector // implementations for Vec<T> and [T;N] after the main() trait ArrayUninit<T>: AsMut<[T]> + Sized { type Uninited: Sized; fn try_uninit(len: usize) -> Result<Self::Uninited,String>; unsafe fn set(uninit: &mut Self::Uninited, i: usize, t: T); unsafe fn destructor(uninit: &mut Self::Uninited,); unsafe fn finalize(uninit: Self::Uninited) -> Self; } // generate shuffled indices fn indices(len: usize) -> Box<dyn Iterator<Item = usize>> { let mut vec: Vec<usize> = (0..len).collect(); vec.shuffle(&mut thread_rng()); Box::new(vec.into_iter()) } // try to build an array or a vector of objects T fn try_new<T: TryFrom<()>, A:ArrayUninit<T>>(len: usize) -> Result<A,String> { // build uninitialized collection let mut uninited = A::try_uninit(len)?; // simulate initialization in random order let indices = indices(len); let mut failed = false; for i in indices { // get object and break if failed let data = match T::try_from(()) { Ok(data) => { data }, Err(_) => { failed = true; break; }, }; // set object unsafe { A::set(&mut uninited,i,data) }; } if !failed { Ok(unsafe{ A::finalize(uninited) }) // return array, if successful } else { unsafe { A::destructor(&mut uninited) }; Err(format!("failed to init")) } } // Object Data #[derive(Debug)] struct Data(String); impl TryFrom<()> for Data { type Error = (); // generate a float with errors; time consuming fn try_from(_:()) -> Result<Self,()> { thread::sleep(Duration::from_millis(10)); let f:f32 = rand::random(); if f <= 0.99 { Ok(Data(format!("Value = {}",f))) } else { Err(()) } } } fn main() { let result: Result<Vec<Data>,_> = try_new(3); println!("result: {:?}",result); let result: Result<[Data;3],_> = try_new(3); println!("result: {:?}",result); let result: Result<Vec<Data>,_> = try_new(3); println!("result: {:?}",result); let result: Result<[Data;3],_> = try_new(3); println!("result: {:?}",result); let result: Result<Vec<Data>,_> = try_new(1000); println!("result: {:?}",result); let result: Result<[Data;1000],_> = try_new(1000); println!("result: {:?}",result); let result: Result<Vec<Data>,_> = try_new(1000); println!("result: {:?}",result); let result: Result<[Data;1000],_> = try_new(1000); println!("result: {:?}",result); } impl<T> ArrayUninit<T> for Vec<T> { type Uninited = (Vec<T>,Vec<bool>); fn try_uninit(len: usize) -> Result<Self::Uninited,String> { Ok((Vec::with_capacity(len),vec![false;len])) } unsafe fn set((uninit,flag): &mut Self::Uninited, i: usize, t: T) { uninit.as_mut_ptr().offset(i as isize).write(t); flag[i] = true; } unsafe fn destructor((uninit,flag): &mut Self::Uninited,) { for i in 0..flag.len() { if flag[i] { std::ptr::drop_in_place(uninit.as_mut_ptr().offset(i as isize)); } } } unsafe fn finalize((mut uninit,flag): Self::Uninited) -> Self { uninit.set_len(flag.len()); uninit } } impl<T,const N: usize> ArrayUninit<T> for [T;N] { type Uninited = ([MaybeUninit<T>;N],[bool;N]); fn try_uninit(len: usize) -> Result<Self::Uninited,String> { if len == N { let uninit = unsafe{ MaybeUninit::uninit().assume_init() }; Ok((uninit,[false;N])) } else { Err(format!("len differs from array size")) } } unsafe fn set((uninit,flag): &mut Self::Uninited, i: usize, t: T) { uninit[i].write(t); flag[i] = true; } unsafe fn destructor((uninit,flag): &mut Self::Uninited,) { for i in 0..N { if flag[i] { std::ptr::drop_in_place(uninit[i].as_mut_ptr()); } } } unsafe fn finalize((uninit,_): Self::Uninited) -> Self { (&uninit as *const _ as *const Self).read() } }
这里的想法是对数组和向量使用特定的方法,这些方法在特征

ArrayUninit

中进行编码。 
MaybeUninit
 仅用于数组,而 vec 则不需要。

rust unsafe maybeuninit
1个回答
1
投票
您的代码包含多个UB点:

    当范围内的元素未初始化时调用
  • set_len()
    (您在
    try_uninit()
    中为
    Vec<T>
    执行此操作)是UB(请参阅
    set_len()
    的文档
    )。
  • 初始化数组时,您在
  • try_uninit()
     中为数组创建未初始化的存储,然后将其转换为对 
    try_new()
     中已初始化数组的引用。这可能是未定义的行为(但不一定),请参阅
    https://github.com/rust-lang/unsafe-code-guidelines/issues/84
  • 在索引处设置值时(
  • unsafe { *ptr_arr = data }
    中的
    try_new()
    ),您将删除旧值。如果该值没有滴胶,这可能没问题,但如果有,则这是未定义的行为,因为您滴下了未初始化的数据。您需要使用 
    std::ptr::write()
     来代替。
  • 您正在通过
  • drop(unsafe { ptr_arr.read() })
     进行值的键入副本。对未初始化值进行类型化复制绝对是 UB(Miri 甚至标记了这一点)。
© www.soinside.com 2019 - 2024. All rights reserved.