如何避免不必要的匹配检查或在从实现Drop特性的结构中移动值时使用不安全的方法

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

我具有使用方法B实现特征Trait的结构do_something。如果尚未调用struct B,则需要执行一些其他操作。具体来说,如果从未调用过do_something,则应该用Vec<A>填充A::None

enum A {
    V1,
    V2,
    None,
}

struct B {
    data: Option<(A, Vec<A>)>,
}

trait Trait {
    fn do_something(self) -> Vec<A>;
}

impl Trait for B {
    fn do_something(mut self) -> Vec<A> {
        let (a, mut vec) = self.data.take().unwrap();
        vec.push(a);
        vec
    }
}

impl Drop for B {
    fn drop(&mut self) {
        match self.data.take() {
            Some((a, mut vec)) => vec.push(A::None),
            _ => {}
        }
    }
}

这在逻辑上是不必要的match检查。我想避免它们,并提出以下解决方案:

struct B {
    data: (A, Vec<A>),
}

trait Trait {
    fn do_something(self) -> Vec<A>;
}

impl Trait for B {
    fn do_something(mut self) -> Vec<A> {
        let (a, mut vec) = std::mem::replace(&mut self.data, unsafe {
            std::mem::MaybeUninit::<(A, Vec<A>)>::uninit().assume_init()
        });
        std::mem::forget(self);
        vec.push(a);
        vec
    }
}

impl Drop for B {
    fn drop(&mut self) {
        self.data.1.push(A::None)
    }
}
  1. 我的unsafe解决方案正确吗?它是否包含未定义的行为?
  2. 是否有可能避免使用unsafe或在B.data中包装Option来实现上述行为?
rust traits unsafe
1个回答
-1
投票

这不仅将值移出结构,还将未初始化的存储器in]写入结构。编译器警告这将导致未定义的行为:

warning: the type `(A, std::vec::Vec<A>)` does not permit being left uninitialized
  --> src/main.rs:21:22
   |
21 |             unsafe { std::mem::MaybeUninit::<(A, Vec<A>)>::uninit().assume_init() },
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                      |
   |                      this code causes undefined behavior when executed
   |                      help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
   |
   = note: `#[warn(invalid_value)]` on by default
note: std::ptr::Unique<A> must be non-null (in this struct field)

更好的解决方案是使用mem::transmute()

impl Trait for B {
    fn do_something(self) -> Vec<A> {
        let (a, mut vec): (A, Vec<A>) = unsafe { std::mem::transmute(self) };
        vec.push(a);
        vec
    }
}

注意,这会阻止调用Drop。如果Drop实现释放了内存或其他资源,则必须手动执行此操作。

© www.soinside.com 2019 - 2024. All rights reserved.