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
实现trait
trait
。如果未调用此函数,则在删除struct
B
时,我需要执行一些附加操作。具体来说,如果从未调用过
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
        }
    }