我有一个具有6个相同类型字段的结构。我希望能够从枚举中获取对它们的可变引用。
例如,给出以下简化定义:
struct S {
a: i32,
b: i32,
cs: [i32; 3],
d: i32,
}
enum E {
A,
B,
COrD(COrD)
}
enum COrD {
C1,
C2,
C3,
D,
}
我想编写这样的函数:
fn get_refs_mut<'a, T>(s: &'a mut S, e1: E, e2: E) -> RefsMut<'a, i32> {
match (e1, e2) {
(E::A, E::A) => RefsMut::Same(&mut S.a),
(E::A, E::B) => RefsMut::Pair(&mut S.a, &mut S.b),
(E::B, E::COrD(COrD::C1)) => RefsMut::Pair(&mut S.b, &mut S.c[0])
// ...
(E::B, E::A) => RefsMut::Pair(&mut S.b, &mut S.a),
(E::B, E::B) => RefsMut::Same(&mut S.b),
(E::B, E::COrD(COrD::C1)) => RefsMut::Pair(&mut S.b, &mut S.c[0]),
// ...
(E::COrD(COrD::D), E::COrD(COrD::D)) => RefsMut::Same(&mut S.d),
}
}
enum RefsMut<'a, T> {
Pair(&'a mut T, &'a mut T),
Same(&'a mut T),
}
但是据我所知,这将需要我手动指定(e1, e2)
的所有6 * 6 = 36个可能值的结果,这很费力。有什么办法可以更简洁地编写此功能?
此外:我定义S
和E
的特定方式可能看起来很奇怪。我有理由在实际代码中使用COrD
的等效项,因此我可以分别引用这些特定字段和相关字段。同样,cs
的真实版本也一起被引用,因此它们在数组中。
答案 0 :(得分:4)
首先编写一个获取对单个成员的引用的函数。然后两次调用该函数:
fn get_ref_mut<'a>(s: &'a mut S, e: E) -> &'a mut i32 {
match e {
E::A => &mut s.a,
E::B => &mut s.b,
E::COrD(c) => match c {
COrD::C1 => &mut s.cs[0],
COrD::C2 => &mut s.cs[1],
COrD::C3 => &mut s.cs[2],
COrD::D => &mut s.d,
}
}
}
fn get_refs_mut<'a>(s: &'a mut S, e1: E, e2: E) -> RefsMut<'a, i32> {
if e1 == e2 {
RefsMut::Same(get_ref_mut(s, e1))
} else {
let first: &mut i32 = unsafe {
&mut *(get_ref_mut(s, e1) as *mut i32)
};
let second = get_ref_mut(s, e2);
RefsMut::Pair(first, second)
}
}
unsafe
块是必需的,因为我们需要将引用中的一个转换为指针,然后取消引用,才能使借阅检查器误认为first
没有引用s
。这样,它将使我们在second
中第二次借用它。但是我们的代码仍然是安全的,因为我们已经以一定的方式构造了代码,确保可以引用s
的2个不同部分。如split_at_mut
所述,这与The Rustonomicon的操作类似。
请注意,表达式e1 == e2
要求您为PartialEq
和E
实现(或派生)COrD
。如果由于某些原因您无法执行此操作,则可以将first
和second
作为指针进行比较,以查看它们是否指向同一事物。