3

I have a boxed tuple to avoid recursion. However, when I pattern match against the tuple, I can't seem to get at both tuple values. To illustrate my problem, take the following code:

#[derive(Clone, PartialEq, Debug)]
enum Foo {
    Base,
    Branch(Box<(Foo, Foo)>),
}

fn do_something(f: Foo) -> Foo {
    match f {
        Foo::Base => Foo::Base,
        Foo::Branch(pair) => {
            let (f1, f2) = *pair;
            if f2 == Foo::Base {
                f1
            } else {
                f2
            }
        }
    }
}

fn main() {
    let f = Foo::Branch(Box::new((Foo::Base, Foo::Base)));
    println!("{:?}", do_something(f));
}

I get this error:

error[E0382]: use of moved value: `pair`
  --> src/main.rs:11:22
   |
11 |             let (f1, f2) = *pair;
   |                  --  ^^ value used here after move
   |                  |
   |                  value moved here
   |
   = note: move occurs because `pair.0` has type `Foo`, which does not implement the `Copy` trait

I've read about boxed syntax, but I'd like to avoid unstable features if at all possible. It feels like the only answer is redefining Branch as

Branch(Box<Foo>, Box<Foo>)

but this seems like it avoids answering the question (which is admittedly mostly a thought exercise at this point).

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Mokosha
  • 2,737
  • 16
  • 33
  • 3
    Given the answers, I would expect this qualifies as a bug of rustc. You might want to open a bug report so that it is checked and fixed. – Matthieu M. Dec 15 '15 at 15:02

3 Answers3

3

This was fixed when non-lexical lifetimes were introduced. The original code now compiles as-is:

#[derive(Clone, PartialEq, Debug)]
enum Foo {
    Base,
    Branch(Box<(Foo, Foo)>),
}

fn do_something(f: Foo) -> Foo {
    match f {
        Foo::Base => Foo::Base,
        Foo::Branch(pair) => {
            let (f1, f2) = *pair;
            if f2 == Foo::Base {
                f1
            } else {
                f2
            }
        }
    }
}

fn main() {
    let f = Foo::Branch(Box::new((Foo::Base, Foo::Base)));
    println!("{:?}", do_something(f));
}

This was tracked in issue 16223.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
1

Unboxing in two steps works:

fn do_something(f: Foo) -> Foo {
    match f {
        Foo::Base => Foo::Base,
        Foo::Branch(pair) => {
            let pair = *pair;
            let (f1, f2) = pair;
            if f2 == Foo::Base {
                f1
            } else {
                f2
            }
        }
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
1

Enclosing within curly braces also works.

fn do_something(f: Foo) -> Foo {
    match f {
        Foo::Base => Foo::Base,
        Foo::Branch(pair) => {
            let (f1, f2) = { *pair };
            if f2 == Foo::Base {
                f1
            } else {
                f2
            }
        }
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
basic_bgnr
  • 697
  • 5
  • 14