将字段移动到新变量时更改枚举变量 [英] Change enum variant while moving the field to the new variant

查看:30
本文介绍了将字段移动到新变量时更改枚举变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想更新枚举变体,同时将旧变体的字段移动到新变体而不进行任何克隆:

I want to update an enum variant while moving a field of the old variant to the new one without any cloning:

enum X {
    X1(String),
    X2(String),
}

fn increment_x(x: &mut X) {
    *x = match *x {
        X::X1(s) => X::X2(s),
        X::X2(s) => X::X1(s),
    }
}

这不起作用,因为我们不能从 &mut X 移动 s:

This doesn't work because we can't move s from &mut X:

error[E0507]: cannot move out of borrowed content
 --> src/lib.rs:7:16
  |
7 |     *x = match *x {
  |                ^^
  |                |
  |                cannot move out of borrowed content
  |                help: consider removing the `*`: `x`
8 |         X::X1(s) => X::X2(s),
  |               - data moved here
9 |         X::X2(s) => X::X1(s),
  |               - ...and here

请不要建议诸如实现 enum X { X1, X2 } 和使用 struct S { variant: X, str: String } 等.这是一个简化的例子,想象一下在变体中有很多其他字段,并且想要将一个字段从一个变体移动到另一个.

Please don't suggest things like implementing an enum X { X1, X2 } and using struct S { variant: X, str: String } etc. This is a simplified example, imagine having lots of other fields in variants, and wanting to move one field from one variant to another.

推荐答案

这不起作用,因为我们不能从 &mut X 移动 s.

然后不要那样做...按值获取结构并返回一个新的:

Then don't do that... take the struct by value and return a new one:

enum X {
    X1(String),
    X2(String),
}

fn increment_x(x: X) -> X {
    match x {
        X::X1(s) => X::X2(s),
        X::X2(s) => X::X1(s),
    }
}

最终,编译器会保护您,因为如果您可以将字符串移出枚举,那么它将处于某种半构造状态.如果函数在那个时刻发生恐慌,谁来负责释放字符串?它应该释放枚举中的字符串还是局部变量中的字符串?它不能两者兼而有之,因为双重释放是一个内存安全问题.

Ultimately, the compiler is protecting you because if you could move the string out of the enumeration, then it would be in some half-constructed state. Who would be responsible for freeing the string if the function were to panic at that exact moment? Should it free the string in the enum or the string in the local variable? It can't be both as a double-free is a memory-safety issue.

如果您必须在可变引用上实现它,您可以在其中临时存储一个虚拟值:

If you had to implement it on a mutable reference, you could store a dummy value in there temporarily:

use std::mem;

fn increment_x_inline(x: &mut X) {
    let old = mem::replace(x, X::X1(String::new()));
    *x = increment_x(old);
}

创建一个空的 String 还不错(它只是几个指针,没有堆分配),但并不总是可能的.在这种情况下,您可以使用 Option:

Creating an empty String isn't too bad (it's just a few pointers, no heap allocation), but it's not always possible. In that case, you can use Option:

fn increment_x_inline(x: &mut Option<X>) {
    let old = x.take();
    *x = old.map(increment_x);
}

另见:

这篇关于将字段移动到新变量时更改枚举变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆