-1

There are a number of questions on this site about pattern matching Box<>, and it currently can't be done on stable Rust (one, two, three etc.). But those questions are all about single levels of box. I have a deeply nested Box/Enum tree. Basically I want to do this:

match node {
    ConstantExpression::ConstantPrimary(Box(ConstantPrimary::PrimaryLiteral(Box(
        PrimaryLiteral::Number(Box(Number::IntegralNumber(Box(
            IntegralNumber::DecimalNumber(Box(DecimalNumber::UnsignedNumber(unsigned_number))),
        )))),
    )))) => Some(unsigned_number),
    _ => None,
};

Is there a way to do this on stable Rust that isn't extremely verbose?

(Edit: This is not a duplicate of this question; I already know you can't do it using match. I was asking for an alternative way to do it.)

Timmmm
  • 88,195
  • 71
  • 364
  • 509
  • 1
    If you can't match on a single level of boxes without `box_patterns` what makes you think that multiple levels are possible? – cafce25 Apr 19 '23 at 09:59
  • I don't think that multiple levels are possible. I was asking for an alternative (see @Sven Marnach's answer). – Timmmm Apr 19 '23 at 12:59
  • Sven's answer is just application of the answers you found yourself. Maybe with the new syntax but still... – cafce25 Apr 19 '23 at 13:02
  • Can you point to the existing answer that has code like Sven's? – Timmmm Apr 19 '23 at 13:34
  • [This one](https://stackoverflow.com/a/66638254/442760) match, dereference and match again, just Sven does it 6 times. – cafce25 Apr 19 '23 at 13:36
  • If I blindly applied that I would end up with a 7-level nested `if` which is not very nice. Sven's is better because it uses early returns. Maybe you think that is obvious, but StackOverflow is not only for questions that *you* think are unobvious. Please give more benefit of the doubt in future, thank you. – Timmmm Apr 19 '23 at 13:48

1 Answers1

2

The problem of matching against boxes aside, there isn't really any redundant information in your pattern. You have to mention each enum variant at least once to specify what you want. The best you can do to make the pattern less unwieldy is doing one level at a time:

pub fn extract_unsigned_number(node: ConstantExpression) -> Option<UnsignedNumber> {
    let ConstantExpression::ConstantPrimary(constant_primary) = node else { return None };
    let ConstantPrimary::PrimaryLiteral(primary_literal) = *constant_primary else { return None };
    let PrimaryLiteral::Number(number) = *primary_literal else { return None };
    let Number::IntegralNumber(integral_number) = *number else { return None };
    let IntegralNumber::DecimalNumber(decimal_number) = *integral_number else { return None };
    let DecimalNumber::UnsignedNumber(unsigned_number) = *decimal_number else { return None };
    Some(*unsigned_number)
}

You could make this slightly less repetitive with a declarative macro, but the code will become more obscure, so I'd personally just use this code as it is.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • Yep that's [what I came up with](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=8866fa11a2de783698fc8131e0e9179a) as well. – cafce25 Apr 19 '23 at 10:49
  • @cafce25 I had to roll back your change. These types are all from the `sv-parser` crate, and `unsigned_number` will have the type `Box`, so the code won't compile anymore with your change. If we want to exactly match the `unsigned_number` in the question, we need to change the return type of the function to `Option>` and change the last line to `Some(unsigned_number)`, but your change in isolation doesn't work. – Sven Marnach Apr 19 '23 at 10:57
  • Oh didn't realize they were all from a crate and there is no indication in the OP that `unsigned_integer` is in a `Box`, too, in that case the dereference makes sense. – cafce25 Apr 19 '23 at 11:07