Pattern guard problem replacing FP literal pattern

I have a little problem with pattern matching that contains floating point values. This is a reduction of code that was OK on the Nightly I use:

#![feature(box_patterns, box_syntax)]
#![allow(illegal_floating_point_literal_pattern)]
#![allow(dead_code)]

enum Exp {
    Const(f64),
    Sum(Box<Exp>, Box<Exp>)
}

fn simplify(e: Exp) -> Exp {
    use Exp::*;
    match e {
        Sum(box Const(0.0), box x) | Sum(box x, box Const(0.0))
            => simplify(x),
        Sum(box Const(l), box Const(r))
            => Const(l + r),
        _ => e,
    }
}

fn main() {}

Those FP patterns will go away, so I've tried to replace them like this:

#![feature(box_patterns, box_syntax)]
#![allow(dead_code)]

enum Exp {
    Const(f64),
    Sum(Box<Exp>, Box<Exp>)
}

fn simplify(e: Exp) -> Exp {
    use Exp::*;
    match e {
        Sum(box Const(z), box x) | Sum(box x, box Const(z))
            if z == 0.0 => simplify(x),
        Sum(box Const(l), box Const(r))
            => Const(l + r),
        _ => e,
    }
}

fn main() {}

But I get errors:

error[E0008]: cannot bind by-move into a pattern guard
  --> ...\test.rs:12:31
   |
12 |         Sum(box Const(z), box x) | Sum(box x, box Const(z))
   |                               ^ moves value into pattern guard

error[E0008]: cannot bind by-move into a pattern guard
  --> ...\test.rs:12:44
   |
12 |         Sum(box Const(z), box x) | Sum(box x, box Const(z))
   |                                            ^ moves value into pattern guard

The pattern guard is "if z == 0.0" it uses just z and a FP contanst, it doesn't touch the x value.

Do you know how to solve this? Thank you.

2 Likes

Indeed, it seems like the x shouldn't move into the match guard. One (very lame) workaround would be to derive Clone on Exp, capture box ref x, and then call simplify(x.clone()) in the arm. As mentioned, this is really unsatisfactory on many levels but thought I'd throw it out there anyway :slight_smile:

2 Likes

This is caused by if taking ownership of x (which was unboxed).

The solution to a problem is not pretty, but you can pattern match again.

#![feature(box_patterns, box_syntax)]
#![allow(dead_code)]

enum Exp {
    Const(f64),
    Sum(Box<Exp>, Box<Exp>),
}

fn simplify(e: Exp) -> Exp {
    use Exp::*;
    match e {
        Sum(box Const(z), _) |
        Sum(_, box Const(z)) if z == 0.0 => {
            match e {
                Sum(box Const(_), box x) |
                Sum(box x, _) => simplify(x),
                _ => unreachable!(),
            }
        }
        Sum(box Const(l), box Const(r)) => Const(l + r),
        _ => e,
    }
}

fn main() {}

Or hiding it behind a macro:

#![feature(box_patterns, box_syntax)]
#![allow(dead_code)]

macro_rules! enum_unwrap {
    ($e:expr, $($p:pat)|+ => $result:expr) => {
        match $e {
            $($p)|+ => $result,
            _ => unreachable!(),
        }
    }
}

enum Exp {
    Const(f64),
    Sum(Box<Exp>, Box<Exp>),
}

fn simplify(e: Exp) -> Exp {
    use Exp::*;
    match e {
        Sum(box Const(z), _) |
        Sum(_, box Const(z)) if z == 0.0 => {
            enum_unwrap!(e, Sum(box Const(_), box x) | Sum(box x, _) => simplify(x))
        }
        Sum(box Const(l), box Const(r)) => Const(l + r),
        _ => e,
    }
}

fn main() {}
1 Like

Should this be the case though? Nice workaround, but it's gnarly/very unergonomic :slight_smile:

1 Like