How can I pattern match a tuple containing a &mut

2019-08-15 04:26发布

问题:

How can the code below be made to compile? It seems perfectly safe, but I can't convince the compiler that it is.

The version matching *self gives the error:

error[E0507]: cannot move out of borrowed content
 --> src/main.rs:8:16
  |
8 |         match (*self, y) {
  |                ^^^^^ cannot move out of borrowed content

The version matching self gives:

error[E0382]: use of moved value: `*self`
  --> src/main.rs:17:26
   |
8  |         match (self, y) {
   |                ---- value moved here
...
17 |                 (*a * b, self)
   |                          ^^^^ value used here after move
   |
   = note: move occurs because `self` has type `&'a mut Foo<'a>`, which does not implement the `Copy` trait
enum Foo<'a> {
    Foo1(Option<&'a mut Foo<'a>>),
    Foo2(i16),
}

impl<'a> Foo<'a> {
    fn bar(&'a mut self, y: i16) -> (i16, &'a mut Foo<'a>) {
        match (self, y) {
            (&mut Foo::Foo1(Some(ref mut a)), b) if (b == 5) => {
                return a.bar(y)
            },

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

                (*a * b, self)
            },

            ref mut x => {
                print!("is not five!");
                (y, self)
            }
        }
    }
}

I feel like I would need a match arm such as the following, but it doesn't seem to be valid syntax:

(ref mut f @ Foo::Foo1, b) if (b == 5) => {
    print!("is five");
    f.0 = b + 42;
    (b, f)
} 
error[E0532]: expected unit struct/variant or constant, found tuple variant `Foo::Foo1`
  --> src/main.rs:24:30
   |
24 |                 (ref mut f @ Foo::Foo1, b) if (b == 5) => {
   |                              ^^^^^^^^^ not a unit struct/variant or constant

This is a dumbed down version of a deep_fetch_mut of a toml::Value that I am trying to write. The goal would be to be able to call .deep_fetch_mut(vec!["aaa","bbb","ccc"]), that will return a mutable reference to that value inside the toml::Value.

This question is an extension of How can I pattern match a tuple containing a &mut enum and use the enum in the match arm?

回答1:

This seems to compile, but it's very ugly. Is there a way to simplify this?

enum Foo<'a> {
    Foo1(Option<&'a mut Foo<'a>>),
    Foo2(i16),
}

impl<'a> Foo<'a> {
    fn bar(&'a mut self, y: i16) -> (i16, &'a mut Foo<'a>) {
        match (&mut *self, y) {
            (Foo::Foo1(Some(ref mut a)), b) if (b == 5) => {
                return a.bar(y)
            },

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

                        *a * b
                    },

                    ref mut x => {
                        print!("is not five!");
                        y
                    }
                };

                return (n, self2)
            }
        }
    }
}