You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: expr.rs
    path of file: ./repos/polars/crates/polars-plan/src/logical_plan/visitor
    
    the code of the file until where you have to start completion: use polars_core::prelude::{Field, Schema};
use polars_utils::unitvec;

use super::*;
use crate::prelude::*;

impl TreeWalker for Expr
    
    fn is_equal(&self, other: &Self, scratch1: &mut Vec<Node>, scratch2: &mut Vec<Node>) -> bool {
        self.with_arena(|arena| {
            let self_ae = self.to_aexpr();
            let other_ae = arena.get(other.node());

            use AExpr::*;
            let this_node_equal = match (self_ae, other_ae) {
                (Alias(_, l), Alias(_, r)) => l == r,
                (Column(l), Column(r)) => l == r,
                (Literal(l), Literal(r)) => l == r,
                (Nth(l), Nth(r)) => l == r,
                (Window { options: l, .. }, Window { options: r, .. }) => l == r,
                (
                    Cast {
                        strict: strict_l,
                        data_type: dtl,
                        ..
                    },
                    Cast {
                        strict: strict_r,
                        data_type: dtr,
                        ..
                    },
                ) => strict_l == strict_r && dtl == dtr,
                (Sort { options: l, .. }, Sort { options: r, .. }) => l == r,
                (Gather { .. }, Gather { .. })
                | (Filter { .. }, Filter { .. })
                | (Ternary { .. }, Ternary { .. })
                | (Len, Len)
                | (Slice { .. }, Slice { .. })
                | (Explode(_), Explode(_)) => true,
                (SortBy { descending: l, .. }, SortBy { descending: r, .. }) => l == r,
                (Agg(l), Agg(r)) => l.equal_nodes(r),
                (
                    Function {
                        function: fl,
                        options: ol,
                        ..
                    },
                    Function {
                        function: fr,
                        options: or,
                        ..
                    },
                ) => fl == fr && ol == or,
                (AnonymousFunction { .. }, AnonymousFunction { .. }) => false,
                (BinaryExpr { op: l, .. }, BinaryExpr { op: r, .. }) => l == r,
                _ => false,
            };

            if !this_node_equal {
                return false;
            }

            self_ae.nodes(scratch1);
            other_ae.nodes(scratch2);

            loop {
                match (scratch1.pop(), scratch2.pop()) {
                    (Some(l), Some(r)) => {
                        // SAFETY: we can pass a *mut pointer
                        // the equality operation will not access mutable
                        let l = unsafe { AexprNode::from_raw(l, self.arena) };
                        let r = unsafe { AexprNode::from_raw(r, self.arena) };

                        if !l.is_equal(&r, scratch1, scratch2) {
                            return false;
                        }
                    },
                    (None, None) => return true,
                    _ => return false,
                }
            }
        })
    }