Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement combine_substructure helper #27

Closed
dtolnay opened this issue Oct 5, 2016 · 3 comments
Closed

Implement combine_substructure helper #27

dtolnay opened this issue Oct 5, 2016 · 3 comments

Comments

@dtolnay
Copy link
Owner

dtolnay commented Oct 5, 2016

Based on the one from libsyntax.

@dtolnay
Copy link
Owner Author

dtolnay commented Oct 9, 2016

@mystor has a promising implementation in abomonation_derive which he plans to factor into a separate crate. I will keep this ticket open until we have something to point people to. Thanks Michael!

@mcarton
Copy link

mcarton commented Oct 9, 2016

Just one remark though: @mystor's implementation currently only allows bindings to be named __binding_{i} where i starts at 0.
In order to implement something like PartialEq you need two matches like that:

    fn eq(&self, __arg_0: &Foo<T>) -> bool {
        match *__arg_0 {
            Foo { foo: ref __self_1_0, bar: ref __self_1_1 } =>
            match *self {
                Foo { foo: ref __self_0_0, bar: ref __self_0_1 } =>
                true && (*__self_0_0) == (*__self_1_0) &&
                    (*__self_0_1) == (*__self_1_1),
            },
        }
    }

(this is what derive(PartialEq) actually generates)

It would be nice if such a helper allowed for a custom placeholder name.

@mystor
Copy link
Collaborator

mystor commented Oct 9, 2016

Firstly, I split my first implementation into its own crate, you can find it here: https://github.com/mystor/synstructure or on crates.io.

@mcarton: I don't currently have support for that, but you can kinda do it yourself, by generating your own let bindings in the outer scope, so you would generate code which looks something like:

    fn eq(&self, __arg_0: &Foo<T>) -> bool {
        match *__arg_0 {
            Foo { foo: ref __binding_0, bar: ref __binding_1 } => {
                let __self_1_0 = __binding_0;
                let __self_1_1 = __binding_1;
                match *self {
                    Foo { foo: ref __binding_0, bar: ref __binding_1 } =>
                    true && (*__binding_0) == (*__self_1_0) &&
                        (*__binding_1) == (*__self_1_1),
                }
            }
        }
    }

I'll admit that that is pretty gross though. I don't really want to add a ton of arguments to these methods because that feels like it will clutter up the interface. It might be easier to just add some sort of helper method which just "renames" the bindings for you with let bindings (I can't imagine those have any performance overhead after optimization). If such a method existed, an eq derive would look something like the following (I haven't tested this, so no promises that I didn't screw something up):

fn derive_partialeq(input: TokenStream) -> TokenStream {
    let source = input.to_string();
    let mut ast = syn::parse_macro_input(&source).unwrap();
    // We need to copy the AST for the inner body, because match_substructs
    // takes a mutable reference. It's kinda gross, and optimized for the case
    // where we want to be able to remove attributes from within the callback.
    // I might change this in the future.
    let mut ast2 = ast.clone();

    let body = match_substructs(&mut ast, bind, |outer_bis| {
        let (renaming, outer_bis) = rename_bindings(outer_bis, "outer");
        let inner_body = match_substructs(&mut ast, bind, |inner_bis| {
            let mut zipped = outer_bis.into_iter().zip(inner_bis.into_iter());
            let eqs = zipped.map(|(x, y)| quote!((#x == #y)));
            quote!(true #(&& #eqs)*)
        })
        quote!{
            #renaming
            match *self { #inner_body }
        }
    }):

    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
    let result = quote! {
        #ast

        impl #impl_generics PartialEq for #name #ty_generics #where_clause {
            fn eq(&self, __arg_0: &Self) -> bool {
                match *__arg_0 { #body }
            }
        }
    };

    result.to_string().parse().unwrap()
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants