切换Rust枚举变体时移除.clone()调用的可行方案
解决TaskState枚举take_todo方法避免clone的方案
你的核心需求是移除不必要的clone()调用,同时不需要让S实现Default trait,最适合的工具是std::mem::replace——它可以直接将当前枚举实例的状态替换为Doing,同时取出原来的S值,全程仅做所有权转移,无额外复制开销。
方案1:使用match匹配实现(最清晰)
impl<S> TaskState<S> { /// Take the spec off of ToDo and change the state to Doing. /// If this wasn't in ToDo, then return [None] fn take_todo(&mut self) -> Option<S> { match self { TaskState::ToDo(_) => { // 将当前状态替换为Doing,拿到原来的ToDo(S)状态 let old_state = std::mem::replace(self, TaskState::Doing); // 从旧状态中取出S值(这里不可能匹配失败,因为刚判断过状态) if let TaskState::ToDo(spec) = old_state { Some(spec) } else { unreachable!("State was confirmed to be ToDo"); } } _ => None, } } }
方案2:基于原if let逻辑修改
如果你想保留原有的if let结构,也可以这样写:
impl<S> TaskState<S> { /// Take the spec off of ToDo and change the state to Doing. /// If this wasn't in ToDo, then return [None] fn take_todo(&mut self) -> Option<S> { if let TaskState::ToDo(_) = self { let old_state = std::mem::replace(self, TaskState::Doing); // 同样,这里的匹配是确定成功的 let TaskState::ToDo(spec) = old_state else { unreachable!("State checked as ToDo"); }; Some(spec) } else { None } } }
关于std::mem::take的补充
如果你的S类型确实可以实现Default trait,也可以用std::mem::take,但这种方式依赖S: Default约束,通用性不如mem::replace:
impl<S: Default> TaskState<S> { /// Take the spec off of ToDo and change the state to Doing. /// If this wasn't in ToDo, then return [None] fn take_todo(&mut self) -> Option<S> { if let TaskState::ToDo(spec) = self { let spec = std::mem::take(spec); // 用Default值替换spec的位置 *self = TaskState::Doing; Some(spec) } else { None } } }
总结:优先选择std::mem::replace的方案,它不依赖任何额外trait约束,性能最优,逻辑也清晰。
内容的提问来源于stack exchange,提问作者dim_voly
相关产品推荐
相关产品推荐

