我正在寻找使用的信息和良好实践
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 则不需要。
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 甚至标记了这一点)。