Search code examples
rustdereference

How to match value of <T> in Box<T> that is attribute of a Struct?


I am trying to parse a simple query using sqlparser crate and I got stuck on BinaryOp struct that is value of Expr enum and the struct itself contains Expr enum.

I am able to print output by matching:

sqlparser::ast::Expr::BinaryOp{left:a , op: b , right: c} => println!("{:?}",a)

But when I try to get Expr from it, it errors with following:

error[E0308]: mismatched types
  --> src/main.rs:41:57
   |
38 | ...                   BinaryOp{left:a , op: b , right: c} => match *a {
   |                                                                    --
   |                                                                    |
   |                                                                    this expression has type `Box<Expr>`
   |                                                                    help: consider dereferencing the boxed value: `**a`
...
41 | ...                       sqlparser::ast::Expr::CompoundIdentifier(w) => {},
   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Box`, found enum `Expr`
   |
   = note: expected struct `Box<Expr>`
                found enum `Expr`

I assume it is the problem with dereference, however I am unable to resolve it even if I added the dereference (*a).

Complete code here:

use sqlparser::dialect::GenericDialect;
use sqlparser::parser::Parser;
use sqlparser::ast::Statement::Query;
use sqlparser::ast::SetExpr;
use sqlparser::ast::Expr::BinaryOp;



fn main() {
    let dialect = GenericDialect {}; // or AnsiDialect

    let sql = "SELECT a,b FROM table_1 T1 left join table_2 T2 on T1.z = T2.z";

    let ast = &Parser::parse_sql(&dialect, sql).unwrap()[0];

    if let Query(x) = ast {
        match &x.body {
            SetExpr::Select(s) => {let projection = &s.projection;
                                   let from = &s.from;
                                   //let group = &s.group_by;

                                   for p in projection {
                                       if let sqlparser::ast::SelectItem::UnnamedExpr(i) = p {
                                           match i {
                                                sqlparser::ast::Expr::Identifier(r) => println!("{:?}", r.value),
                                                _ => {},
                                           };
                                       }
                                   }

                                   for f in from {
                                       //println!("{:?}",f.relation);
                                       for j in &f.joins {
                                           match &j.join_operator {
                                            sqlparser::ast::JoinOperator::Inner(inj) => println!("inner join {:?}",inj),
                                            sqlparser::ast::JoinOperator::LeftOuter(lfj) => match lfj {
                                                sqlparser::ast::JoinConstraint::On(on) => match on {
                                                    BinaryOp{left:a , op: b , right: c} => match *a {
                                                    
                                                        
                                                        sqlparser::ast::Expr::CompoundIdentifier(w) => {},

                                                        _ => {}
                                                    },
                                                _ => {}
                                                },
                                                _ => {}
                                            },
                                            _ => {}
                                            }
                                        }
                                    }
            },                      
            _ => {}
        }
    }
}

  

How it should be done to get the boxed value from BinaryOp struct?

Thank you.


Solution

  • The variable a has type &Box<Expr>, so when you dereference it, *a, it only removes the reference leaving just Box<Expr>. You unfortunately cannot do pattern matching on values in a Box (see How to pattern match a Box to get a struct's attribute?), so you have to dereference it again to get an Expr:

    match **a {
    

    However, then the match arm will try to move the variable w out of **a, which is not allowed since it is behind a shared reference.

    You can either avoid moving entirely by referencing the Expr via &**a or by binding to ref w instead of just w. If this starts looking like too much symbol soup for you, another option is to use a.as_ref() (via the AsRef trait).