The WILD things you can do with the `fn_traits` feature

Here's a wild syntax that doesn't seem possible, but with fn_traits, is!

fn main() {
    rm();
    rm.recursive();
    rm.recursive.dir();
    rm.recursive.dir.verbose();
}

The above could be generated using a simple proc macro:

#[i_am_the_proc_macro]
struct rm {
    recursive: bool,
    dir: bool,
    verbose: bool
}

Once you have rm.recursive, you can only access the dir and verbose flags on it. You can use any order! rm.recursive.dir(), rm.dir.recursive.verbose()...

Where is this useful? I don't know.. maybe a fancy library for ergonomically writing scripts in Rust, that implements this interface for common utilities like ls and rm.

But it will destroy your compile times! Compilation time complexity, with n being the number of fields in your struct, is O(n!)...

This is how it works (Playground):

const rm: rm_base = rm_base {
    recursive: rm_r {
        dir: rm_rd { verbose: rm_rdv {} },
        verbose: rm_rv { dir: rm_rvd {} },
    },
    dir: rm_d {
        recursive: rm_dr { verbose: rm_drv {} },
        verbose: rm_dv {
            recursive: rm_dvr {},
        },
    },
    verbose: rm_v {
        recursive: rm_vr { dir: rm_vrd {} },
        dir: rm_vd {
            recursive: rm_vdr {},
        },
    },
};

Each one of those sub-structs implements the Fn trait

The implementation of the methods is the following:

#![feature(fn_traits)]
#![feature(unboxed_closures)]

macro_rules! node {
    ($name:ident, $r:expr, $d:expr, $v:expr; $($fname:ident: $ftype:ident => ($nr:expr, $nd:expr, $nv:expr)),*) => {
        struct $name { $(pub $fname: $ftype),* }
        impl $name { $(pub fn $fname(self) { })* }
        impl FnOnce<()> for $name { type Output = (); extern "rust-call" fn call_once(self, a: ()) { } }
    };
}

node!(rm_base, false, false, false; recursive: rm_r => (true, false, false), dir: rm_d => (false, true, false), verbose: rm_v => (false, false, true));
node!(rm_r, true, false, false; dir: rm_rd => (true, true, false), verbose: rm_rv => (true, false, true));
node!(rm_d, false, true, false; recursive: rm_dr => (true, true, false), verbose: rm_dv => (false, true, true));
node!(rm_v, false, false, true; recursive: rm_vr => (true, false, true), dir: rm_vd => (false, true, true));
node!(rm_rd, true, true, false; verbose: rm_rdv => (true, true, true));
node!(rm_rv, true, false, true; dir: rm_rvd => (true, true, true));
node!(rm_dr, true, true, false; verbose: rm_drv => (true, true, true));
node!(rm_dv, false, true, true; recursive: rm_dvr => (true, true, true));
node!(rm_vr, true, false, true; dir: rm_vrd => (true, true, true));
node!(rm_vd, false, true, true; recursive: rm_vdr => (true, true, true));
node!(rm_rdv, true, true, true;);
node!(rm_rvd, true, true, true;);
node!(rm_drv, true, true, true;);
node!(rm_dvr, true, true, true;);
node!(rm_vrd, true, true, true;);
node!(rm_vdr, true, true, true;);

It's already possible if you exclude rm() though.

Do you mean if the fields held function pointers? You have to surround fields with parentheses to call them.

fn main() {
    (rm.recursive)();
    (rm.recursive.dir)();
    (rm.recursive.dir.verbose)();
}

And if you can call rm.recursive, it's not possible for callable fields to have accessible fields (so, no rm.recursive.dir) (unless you use the Fn traits)

I mean something like this. (Was going to edit the link in but you replied faster :slight_smile: ...)

you're right! It isn't that wild after all :grinning_face_with_smiling_eyes:

there is a lot of manual typing here.

with smth like this you don't need to write every case by hand

macro_rules! gen_recursive_type {
    ($src_ty:ident { $($field_name:ident),* } $($path_hist:ident),* ) => {
        #[derive(Debug, Default, Clone, Copy)]
        #[allow(unused)]
        pub struct $src_ty {
            $(pub $field_name : $field_name::Ty),*
        }
        
        impl $src_ty {
            #[allow(unused)]
            pub const fn new() -> Self {
                Self {
                    $($field_name : $field_name::Ty::new()),*
                }
            }
            
            #[allow(unused)]
            fn path(self) {
                print!(concat!("called _.", $(stringify!($path_hist) , ".", )* ))
                
            }
            
            
            $(
            #[allow(unused)]
            pub fn $field_name(self) {
                self.path();
                println!(concat!( stringify!($field_name), "() !" ))
            })*
        }
        
        sub_gen!($($field_name),*  <= $($path_hist),*);
    };
}

macro_rules! sub_gen {
    ( <= $($path_hist:ident),* ) => {};
    ($first_name:ident $(,$other:ident)* <= $($path_hist:ident),* ) => {
        sub_gen!($first_name ; ; $($other),*  <= $($path_hist),* );
    };
    ($ignored:ident ; $($prev:ident),* ; <= $($path_hist:ident),* ) => {
        pub mod $ignored {
            gen_recursive_type!(Ty { $($prev),* } $($path_hist,)* $ignored);
        }
    };
    ($ignored:ident ; $($prev:ident),* ; $next:ident $(,$after:ident)*  <= $($path_hist:ident),* ) => {
        pub mod $ignored {
            gen_recursive_type!(Ty { $($prev,)* $next $(,$after)* } $($path_hist,)* $ignored);
        }
        sub_gen!($next ; $($prev,)* $ignored ; $($after),*  <= $($path_hist),*);
    };
}


gen_recursive_type!(A { x, y, z });

const A : A = A::new();

fn main() {
    A.x(); // called _.x() !
    A.y.z(); // called _.y.z() !
    A.z.y.x(); // called _.z.y.x() !
    A.x.z.y(); // called _.x.z.y() !
}

and can use path information to do whatever you want, not just printing

1 Like