在覆盖 self 之前移出枚举中的可变自引用

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

我有一个类似有限状态机的枚举,它具有包含不同类型缓冲区的不同状态。

在我的“转换”方法中,我正在更新一个枚举字段,并有条件地将

self
替换为不同的变体,重用旧的字段值。

但是在一种特定情况下,我想将大缓冲区从旧变体移动到新变体,所检查的借用不允许我这样做。

在避免整个缓冲区的昂贵副本的同时执行此操作的正确方法是什么?

impl EnumBased {
    fn step(&mut self) {
        match self {
            EnumBased::A(..) => { /* do something */ },
            EnumBased::B {
                buffer,  // &mut Buffer
            } => { 
                buffer.do_something()
                if something {
                    // consume buffer, moving out of self
                    let inner = buffer.to_inner();  // <-- at this point, can't move out of a mutable reference
                    // overwrite now-defunct self
                    *self = EnumBased::C { inner, ... }
                }
            },
        }
    }
}
rust enums borrow-checker
1个回答
0
投票

首先,我已将您的代码更改为几乎可以编译的代码。

pub enum EnumBased {
    B { buffer: Vec<u8> },
    C { buffer: Vec<u8> },
}

impl EnumBased {
    pub fn step(&mut self) {
        *self = match self {
            Self::B { buffer } => {
                buffer.pop();
                Self::C { buffer } // expected `Vec<u8>`, found `&mut Vec<u8>`
            }
            _ => todo!(),
        };
    }
}

如果可以廉价地构建缓冲区(如

Vec
),您可以简单地将其替换为空缓冲区。一种方法是使用
std::mem::take

*self = match self {
    Self::B { buffer } => {
        buffer.pop();

        let buffer = std::mem::take(buffer);
        Self::C { buffer }
    }
    _ => todo!(),
};

如果这是不可能的,您可以使您的枚举可以廉价地构建。要么使用现有的廉价变体,要么专门为此目的制作一个新变体。然后您可以使用

std::mem::replace
来短暂替换
self

pub enum EnumBased {
    B { buffer: Vec<u8> },
    C { buffer: Vec<u8> },
    Invalid
}

impl EnumBased {
    pub fn step(&mut self) {
        *self = match std::mem::replace(self, Self::Invalid) {
            Self::B { mut buffer } => {
                buffer.pop();
                Self::C { buffer }
            }
            _ => todo!(),
        };
    }
}

使用专用变体的优点是,如果您在此函数之外看到它,您就知道出了问题。

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