Rust 在从实现Drop的结构中移动值时,如何避免不必要的匹配检查或使用不安全的方法?
我有一个结构Rust 在从实现Drop的结构中移动值时,如何避免不必要的匹配检查或使用不安全的方法?,rust,destructor,unsafe,Rust,Destructor,Unsafe,我有一个结构B,它用方法do\u something实现traittrait。如果未调用此函数,则在删除structB时,我需要执行一些附加操作。具体来说,如果从未调用过do\u something,Vec应该用A::None填充: enum A{ V1, V2, 没有一个 } 结构B{ 数据:选项, } 特质{ fn做某事(自我)->Vec; } B的impl特征{ fn做某事(自我)->Vec{ let(a,mut-vec)=self.data.take().unwrap(); 矢量推力(a
B
,它用方法do\u something
实现traittrait
。如果未调用此函数,则在删除structB
时,我需要执行一些附加操作。具体来说,如果从未调用过do\u something
,Vec
应该用A::None
填充:
enum A{
V1,
V2,
没有一个
}
结构B{
数据:选项,
}
特质{
fn做某事(自我)->Vec;
}
B的impl特征{
fn做某事(自我)->Vec{
let(a,mut-vec)=self.data.take().unwrap();
矢量推力(a);
vec
}
}
B的impl下降{
fn下降(&mut自我){
匹配self.data.take(){
Some((a,mut-vec))=>vec.push(a::None),
_ => {}
}
}
}
这有一些逻辑上不必要的匹配检查。我想避免它们,并提出了以下解决方案:
结构B{
数据:(A,Vec),
}
特质{
fn做某事(自我)->Vec;
}
B的impl特征{
fn做某事(自我)->Vec{
let(a,mut-vec)=std::mem::replace(&mut-self.data,不安全{
std::mem::maybeunit:::uninit()。假设_init()
});
标准::记忆::忘记(自我);
矢量推力(a);
vec
}
}
B的impl下降{
fn下降(&mut自我){
self.data.1.push(A::None)
}
}
不安全解决方案正确吗?它是否包含未定义的行为
选项中使用不安全
或包装B.data
,以实现上述行为
这不仅将值移出结构,还将未初始化的内存写入结构。编译器警告这将导致未定义的行为:
警告:类型`(A,std::vec::vec)`不允许未初始化
-->src/main.rs:21:22
|
21 |不安全{std::mem::maybeunit:::uninit()。假设_init()},
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
|此代码在执行时会导致未定义的行为
|帮助:改为使用'maybeuniit',并且只在初始化完成后调用'ascape_init'
|
=注意:`#[警告(无效的_值)]`默认为打开
注意:std::ptr::Unique必须为非null(在此结构字段中)
更好的解决方案是使用mem::transmute()
:
#[repr(透明)]
结构B{
数据:(A,Vec),
}
B的impl特征{
fn做某事(自我)->Vec{
//安全性:由于'B'是透明的,'B'和(A,Vec)具有相同的大小和布局。
let(a,mut-vec):(a,vec)=不安全{std::mem::transmute(self)};
矢量推力(a);
vec
}
}
请注意,这会阻止调用
Drop
。如果Drop
实现释放内存或其他资源,则必须手动执行此操作。这不仅会将值移出结构,还会将未初始化的内存写入结构。编译器警告这将导致未定义的行为:
警告:类型`(A,std::vec::vec)`不允许未初始化
-->src/main.rs:21:22
|
21 |不安全{std::mem::maybeunit:::uninit()。假设_init()},
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
|此代码在执行时会导致未定义的行为
|帮助:改为使用'maybeuniit',并且只在初始化完成后调用'ascape_init'
|
=注意:`#[警告(无效的_值)]`默认为打开
注意:std::ptr::Unique必须为非null(在此结构字段中)
更好的解决方案是使用mem::transmute()
:
#[repr(透明)]
结构B{
数据:(A,Vec),
}
B的impl特征{
fn做某事(自我)->Vec{
//安全性:由于'B'是透明的,'B'和(A,Vec)具有相同的大小和布局。
let(a,mut-vec):(a,vec)=不安全{std::mem::transmute(self)};
矢量推力(a);
vec
}
}
请注意,这会阻止调用
Drop
。如果Drop
实现释放了内存或其他资源,您必须手动执行此操作。当前代码似乎正确,但也很脆弱-如果您在忘记(self)
之前引入了访问self.data
的内容,您将拥有UB。另外,maybeuniit::uninit()::假设_init()
具有明确的代码气味。既然空向量和A
有一个便宜的None
变体,为什么不使用mem::replace(&mut self.data,(A::None,vec![])
?应该没有运行时成本,没有不安全,代码不再作为UB的吸引器。我很确定这会触发UB(RUSC也会发出警告),因为未初始化的内存会写入self.data
。请仔细阅读。我很感激你创造了一个新的世界,但我认为你可能正在遭受痛苦。现在还不清楚为什么你需要用任何东西来填充向量才能将其丢弃,这表明如果你退一步,有更好的方法来做你想做的事情;在被破坏的Vec
上推(A::None)有什么意义?恐怕你的例子简化得太多了。当前的代码似乎是正确的,但也很脆弱-如果你在忘记(self)
之前引入了一些访问self.data
的东西,你就有UB了。另外,maybeuniit::uninit()::假设_init()
具有明确的代码气味。既然空向量和A
有一个便宜的None
变体,为什么不使用mem::replace(&mut self.data,(A::None,vec![]))
#[repr(transparent)]
struct B {
data: (A, Vec<A>),
}
impl Trait for B {
fn do_something(self) -> Vec<A> {
// Safety: Since `B` is transparent, `B` and (A, Vec<A>) have the same size and layout.
let (a, mut vec): (A, Vec<A>) = unsafe { std::mem::transmute(self) };
vec.push(a);
vec
}
}