在Rust中更新enum变量数据时避免双重可变引用

vc9ivgsu  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(81)

(使用rust 1.74.1)
我有这个设置:

struct DataA {
    a: i32
}

struct DataB {
    b: i32
}

enum Data {
    TypeA(DataA),
    TypeB(DataB),
    // TypeC etc...
}

struct Struct {
    field: i32,
    // other fields
    data: Data,
}

impl Struct {
    fn foo(&mut self) -> i32 {
        self.field += 5;
        return 5;
    }
}

字符串
我想为Struct实现一个方法(bar()),该方法将TypeA(DataA)a设置为foo()的返回值,如果Struct . data不属于TypeA,则不执行任何操作。
最初,我试图

fn bar(&mut self) {
    let Data::TypeA(data) = &mut self.data else {
        return;
    };
    data.a = self.foo();
}


但这不起作用,因为我cannot borrow*selfas mutable more than once at a time
经过一段时间的尝试,我发现了一个错误,

let Data::TypeA(..) = self.data else {
    return;
};
self.data = Data::TypeA(DataA{ a: self.foo() });


这是可行的,但冗长和繁琐(例如,添加更多的字段到DataA)。
请注意,foo()永远不会改变Structdata,并且必须在检查类型是否匹配之后运行。
有没有更好的方法在不改变设置的情况下编写此代码?如果没有,最惯用的方法是什么?

s5a0g9ez

s5a0g9ez1#

请注意,foo()永远不会改变Structdata,并且必须在检查类型是否匹配后运行。
我认为这是主要的问题。虽然你说foo从来没有这样做过,但你的API并没有反映这一点。如果是这样,编译器会让你这样做。
如果您将field封装在它自己的数据类型中,并将foo函数移到那里,您实际上告诉编译器您只在此函数中修改field

struct DataA {
    a: i32,
}

struct DataB {
    b: i32,
}

enum Data {
    TypeA(DataA),
    TypeB(DataB),
    // TypeC etc...
}

struct FieldData {
    field: i32,
}

impl FieldData {
    fn foo(&mut self) -> i32 {
        self.field += 5;
        return 5;
    }
}

struct Struct {
    field: FieldData,
    // other fields
    data: Data,
}

impl Struct {
    fn bar(&mut self) {
        if let Data::TypeA(data) = &mut self.data {
            data.a = self.field.foo();
        }
    }
}

字符串
将一个结构体拆分成多个部分是解决此类问题的一个非常常见的解决方案。
如果你需要对结构体的其他部分进行只读访问,比如前面的DataA对象,你可以通过函数参数传入它:

struct DataA {
    a: i32,
}

struct DataB {
    b: i32,
}

enum Data {
    TypeA(DataA),
    TypeB(DataB),
    // TypeC etc...
}

struct FieldData {
    field: i32,
}

impl FieldData {
    fn foo(&mut self, previous_data: &DataA) -> i32 {
        self.field += 5;
        return 5;
    }
}

struct Struct {
    field: FieldData,
    // other fields
    data: Data,
}

impl Struct {
    fn bar(&mut self) {
        if let Data::TypeA(data) = &mut self.data {
            data.a = self.field.foo(data);
        }
    }
}

相关问题