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

Is there a way to construct mutually recursive structs? #26

Open
teymour-aldridge opened this issue Jan 15, 2022 · 3 comments
Open

Is there a way to construct mutually recursive structs? #26

teymour-aldridge opened this issue Jan 15, 2022 · 3 comments

Comments

@teymour-aldridge
Copy link
Collaborator

e.g. trying to derive DefaultMutator fails (not unexpectedly) on the following structs.

struct MutuallyRecursiveA {
  b: Vec<MutuallyRecursiveB>,
  data: Vec<u64>
}

struct MutuallyRecursiveB {
  a: Option<A>,
  data: bool
}

I tried using make_mutator, but I couldn't get the recursion to work.

Is this possible?

@teymour-aldridge
Copy link
Collaborator Author

teymour-aldridge commented Jan 16, 2022

I think I've solved in my case by reworking the types I'm using to not be mutually recursive.

@loiclec
Copy link
Owner

loiclec commented Jan 17, 2022

It's possible, but it is pushing of the limits of what is currently possible, and I don't know what the quality of the resulting mutators will be (maybe they're fine, I just haven't tested it).

You can try to write something like:

make_mutator! {
    name: AMutator,
    recursive: true,
    default: false,
    type:
        struct MutuallyRecursiveA {
            b: Vec<MutuallyRecursiveB>,
            #[field_mutator(<Vec<u64> as DefaultMutator>::Mutator = { <Vec<u64>>::default_mutator() })]
            data: Vec<u64>,
        }
}

make_mutator! {
    name: BMutator,
    recursive: true,
    default: true,
    type:
        struct MutuallyRecursiveB {
            #[field_mutator(
                OptionMutator<MutuallyRecursiveA, AMutator<VecMutator<MutuallyRecursiveB, RecurToMutator<BMutator>>>>
            ) = {
                OptionMutator::new(AMutator::new(
                    VecMutator::new(self_.into(), 0..=usize::MAX),
                    <Vec<u64>>::default_mutator(),
                ))
            }]
            a: Option<MutuallyRecursiveA>,
            #[field_mutator(<bool as DefaultMutator>::Mutator = { <bool>::default_mutator() })]
            data: bool
        }
}

It is not a perfect solution: MutuallyRecursiveA has no default mutator, only MutuallyRecursiveB does.
You could however, repeat the snippet above twice except that A and B are switched, giving different names to the mutators. The idea is that for each mutually recursive type, you have a “default mutator” and a “mutator to be used only as a submutator to the other type’s default mutator”.

That's not fundamentally necessary though, and I could improve the procedural macro to make it possible to define these mutually recursive mutators using only one make_mutator! per type. I would need to think about those generic bounds a bit more (again! lol).

Note though that due to a questionable design decision (because of superfluous generic bound, again!) , even the code I posted above doesn't actually work. So you have to construct BMutator with:

 let mutator = RecursiveMutator::new(|self_| {
    BMutator::new(
        OptionMutator::new(AMutator::new(
            VecMutator::new(self_.into(), 0..=usize::MAX),
            <Vec<u64>>::default_mutator(),
        )),
        bool::default_mutator(),
    )
});

I'll try and fix that and post the progress in this issue. It may be a LONG time before mutually-recursive types are easy to work with though.

@teymour-aldridge
Copy link
Collaborator Author

teymour-aldridge commented Jan 17, 2022

Thank you so much!

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

No branches or pull requests

2 participants