如何对包含&mut枚举的元组进行模式匹配,并在匹配臂中使用该枚举?

时间:2018-07-13 16:33:06

标签: rust pattern-matching borrow-checker

如何编写以下代码?看起来绝对安全,但是无法说服编译器。

匹配@Builder class SomeObject { @NonNull String mandatoryField1; @NonNull String mandatoryField2; Integer optionalField; ... public static SomeObjectBuilder builder() { //class name convention by Lombok return new CustomBuilder(); } public static class CustomBuilder extends SomeObjectBuilder { private static ValidationFactory vf = Validation.buildDefaultValidationFactory(); private Validator validator = vf.getValidator(); @Overrride public SomeObject build() { SomeObject result = super.build(); validateObject(result); return result; } private void validateObject(Object object) { //if object is null throw new IllegalArgException or ValidationException Set<ConstraintVioletion<Object>> violations = validator.validate(object); if (violations.size() > 0) { //iterate through violations and each one has getMessage(), getPropertyPath() // - to build up detailed exception message listing all violations [...] throw new ValidationException(messageWithAllViolations) } } } 的版本会在匹配行上显示错误:*self

匹配cannot move out of borrowed content的版本给出:self

use of moved value: *self

我觉得我需要一个如下所示的匹配臂,但它似乎不是有效的语法:

enum Foo {
    Foo1(u32),
    Foo2(i16),
}

impl Foo {
    fn bar(&mut self, y: u32) -> (u32, &mut Foo) {
        match (*self, y) {
            (Foo::Foo1(ref mut a), b) if (b == 5) => {
                print!("is five");
                *a = b + 42;

                (b, self)
            }

            (Foo::Foo2(ref mut a), b) if (b == 5) => {
                print!("is five");
                *a = (b + 42) as i16;

                (*a * b, self)
            }

            _ => {
                print!("is not five!");
                (y, self)
            }
        }
    }
}
(ref mut f @ Foo::Foo1, b) if (b == 5) => {
    print!("is five");
    f.0 = b + 42;
    (b, f)
} 

1 个答案:

答案 0 :(得分:2)

不,这不安全。您正在尝试在匹配臂中引入可变别名。可变引用a指向与self相同的值。可以更改self(例如*self = Foo::Foo1(99)),这会使a无效,因此该代码是不允许的。

相反,在self语句中可变地重新借入 match,并使它返回元组的第一个值。由于此值没有引用self,因此您可以返回self并返回match的结果:

enum Foo {
    Foo1(u32),
    Foo2(u32), // changed so I don't have to figure out what casting you meant
}

impl Foo {
   fn bar(&mut self, y: u32) -> (u32, &mut Foo) {
        let next = match (&mut *self, y) {
            (Foo::Foo1(a), b @ 5) => {
                *a = b + 42;
                b
            }

            (Foo::Foo2(a), b @ 5) => {
                *a = b + 42;
                *a * b
            }

            _ => y,
        };

        (next, self)
    }
}

但是,在这里返回self却毫无意义。呼叫者已经已经有了&mut Foo,因此您无需“退还”。这样可以简化为:

impl Foo {
    fn bar(&mut self, y: u32) -> u32 {
         match (self, y) {
            (Foo::Foo1(a), b @ 5) => {
                *a = b + 42;
                b
            }

            (Foo::Foo2(a), b @ 5) => {
                *a = b + 42;
                *a * b
            }

            _ => y,
        }
    }
}
  

我仍然会说这是一个安全的操作,尽管编译器可能无法理解

使用non-lexical lifetimes,借阅检查器变得更加智能。具有原始显式重新借用的原始代码将进行编译:

#![feature(nll)]

enum Foo {
    Foo1(u32),
    Foo2(u32), // changed so I don't have to figure out what casting you meant
}

impl Foo {
   fn bar(&mut self, y: u32) -> (u32, &mut Foo) {
        match (&mut *self, y) {
            (Foo::Foo1(a), b @ 5) => {
                *a = b + 42;
                (b, self)
            }

            (Foo::Foo2(a), b @ 5) => {
                *a = b + 42;
                (*a * b, self)
            }

            _ => (y, self),
        }
    }
}

另请参阅: