这个Rust匹配表达式可以简化吗?

时间:2018-10-31 06:09:01

标签: rust pattern-matching

我有一个具有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个可能值的结果,这很费力。有什么办法可以更简洁地编写此功能?

此外:我定义SE的特定方式可能看起来很奇怪。我有理由在实际代码中使用COrD的等效项,因此我可以分别引用这些特定字段和相关字段。同样,cs的真实版本也一起被引用,因此它们在数组中。

1 个答案:

答案 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要求您为PartialEqE实现(或派生)COrD。如果由于某些原因您无法执行此操作,则可以将firstsecond作为指针进行比较,以查看它们是否指向同一事物。