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

Does not support associated types #57

Open
zhixinwen opened this issue Oct 27, 2023 · 1 comment
Open

Does not support associated types #57

zhixinwen opened this issue Oct 27, 2023 · 1 comment

Comments

@zhixinwen
Copy link

    #[cfg_attr(test, faux::create)]
    struct Object {}

    #[cfg_attr(test, faux::methods)]
    impl Object {
        pub fn method<A>(&self, data: impl IntoIterator<Item = A>) {}
    }
    
    #[test]
    fn test_mock() {
        let o = Object::faux();
    }

Error is

error[E0191]: the value of the associated type `IntoIter` (from trait `IntoIterator`) must be specified
    |
784 |         pub fn method<A>(&self, data: impl IntoIterator<Item = A>) {}
    |                                            ^^^^^^^^^^^^^^^^^^^^^^ help: specify the associated type: `IntoIterator<Item = A, IntoIter = Type>`
@nrxus
Copy link
Owner

nrxus commented Oct 30, 2023

Hmm this is a little tricky. To be specific though faux supports associated types but not elided associated types. IntoIterator has two different associated types (Item + IntoIter), but only one is explicitly stated (Item), meaning that IntoIter is elided and faux doesn't know about it.

As a workaround you could get away with using impl Iterator<Item =A>. It does mean your callers will have to call .into_iter() manually but I don't think that makes the API any less flexible.

The reason why this is tricky is a bit complex but I am going to try to explain it as a reference to myself in the future (or if you're interested in submitting a PR for it ofc you'd be welcome to).

When you have a function like:

fn method<A>(&self, data: impl IntoIterator<Item = A>) {}

Rust will under the hood desugar it to something like:

fn method<A, I>(&self, data: impl IntoIterator<Item = A, IntoIter=I>) {}

Which means that there is a hidden generic there (I), but Rust allows it to elided, which is fine because Rust will know during monomorphization what that hidden generic is. But if we AREN'T doing monomorphization (i.e., using dyn) then Rust wouldn't be able to do that, so dynamic objects (i.e., Box<dyn Trait>) cannot elide associated types.

faux currently handles arguments of the syntax impl Trait by converting them to Box<dyn Trait> when mocking (so the signature of the function remains the same but the signature of how it's mocked uses the dynamic form). Because we've converted it to Box<dyn Trait> we now need to specify the associated type explicitly.

Specifying the associated type is actually not that hard, it just means adding a generic in some internal function that faux generates but the issue is that faux has no way of knowing that it needs to add that extra generic because there is no hint that impl IntoIterator<Item =A> has an elided associated type missing (proc macros work purely at the syntax level and there is nothing in the syntax about the missing associated type). So something else needs to tell faux about this missing associated type, which means I gotta come up with a good syntax for it.

Maybe something like:

#[cfg_attr(test, faux::methods)]
// specifies all associated types of `IntoIterator` so `faux` knows about them. 
#[cfg_attr(test, faux(hint = "IntoIterator<type Item, type IntoIter>"))]
impl Object {
    pub fn method<A>(&self, data: impl IntoIterator<Item = A>) {}
}

But that's just my first idea off the top of my head so I may need to chew on it more to see if i can think of a better UX for this, but something does need to be explicitly given to faux so it knows about the elided associated type.

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