代码之家  ›  专栏  ›  技术社区  ›  Brennan Vincent

用使用上一个值的新值替换结构成员

  •  0
  • Brennan Vincent  · 技术社区  · 6 年前

    我有一个结构,它拥有某个trait类型的装箱值。结构本身也实现了相同的特性。我想用相同结构的一个新实例替换这个值,这个新实例封装了这个值。

    下面的代码没有编译,应该更清楚地说明我要做什么:

    trait T {}
    
    struct S {
        t: Box<dyn T>,
    }
    impl T for S {}
    
    impl S {
        fn new(t: Box<dyn T>) -> Self {
            Self { t }
        }
    
        fn wrap_t(&mut self) {
            self.t = Box::new(Self::new(self.t))
        }
    }
    

    error[E0507]: cannot move out of borrowed content
      --> src/lib.rs:14:37
       |
    14 |         self.t = Box::new(Self::new(self.t))
       |                                     ^^^^ cannot move out of borrowed content
    

    实施 wrap_t 像这样编译:

    use std::mem;
    
    fn wrap_t(&mut self) {
        unsafe {
            let old_t = mem::replace(&mut self.t, mem::uninitialized());
            let new_t = Box::new(Self::new(old_t));
            let uninit = mem::replace(&mut self.t, new_t);
            mem::forget(uninit);
        }
    }
    

    1 回复  |  直到 6 年前
        1
  •  1
  •   Shepmaster Tim Diekmann    6 年前

    唯一的 unsafe mem::uninitialized . 你需要些东西传给我 mem::replace ,但实施 Default default() 退货 Self ,这会阻止它成为对象安全的。同样,您也不能实现 Clone clone() 还返回 自我 .

    struct Dummy;
    impl T for Dummy {}
    
    fn wrap_t(&mut self) {
        let old_t = mem::replace(&mut self.t, Box::new(Dummy));
        let new_t = Box::new(Self::new(old_t));
        mem::replace(&mut self.t, new_t);
    }
    

    mem::forget 现在也是这样(我假设这样做是为了防止在删除未初始化的内存时出现未定义的行为)。


    克隆 ,你可以把自己的克隆到一个 Box<dyn T> ,避免 自我 在方法签名中,以使特征保持对象安全:

    trait T: Debug {
        fn clone_in_box(&self) -> Box<dyn T>;
    }
    
    impl T for S {
        fn clone_in_box(&self) -> Box<dyn T> {
            Box::new(S {
                t: self.t.clone_in_box(),
            })
        }
    }
    
    fn wrap_t(&mut self) {
        let cloned = self.clone_in_box();
        let old_t = mem::replace(&mut self.t, cloned);
        let new_t = Box::new(Self::new(old_t));
        mem::replace(&mut self.t, new_t);
    }
    

    还有另一种设计,在阅读代码时更容易理解。那只是为了消费 self 并返回一个新对象:

    fn wrap_t(self) -> Self {
        Self::new(Box::new(Self::new(self.t)))
    }
    

    而不是这个:

    s.wrap_t();
    

    你应该做:

    s = s.wrap_t();