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

Returning &Self #9

Open
U007D opened this issue Dec 12, 2017 · 6 comments
Open

Returning &Self #9

U007D opened this issue Dec 12, 2017 · 6 comments
Assignees

Comments

@U007D
Copy link

U007D commented Dec 12, 2017

Given the following trait:

pub trait Chal {
    fn set_mech_brakes(&self, amt: BrakeAmt) -> &Self;
    fn mech_brakes(&self) -> BrakeAmt;
}

I have tried:

mock_trait!(ChalMock,
            set_brakes(BrakeAmt) -> &Self,
            brakes() -> BrakeAmt);

impl Chal for ChalMock {
    mock_method!(set_mech_brakes(&self, amt: BrakeAmt) -> &Self);
    mock_method!(mech_brakes(&self) -> BrakeAmt);
}

but this yields:

error[E0411]: cannot find type `Self` in this scope
 --> shal_proto/src/unit_tests.rs:7:38
  |
7 |             set_brakes(BrakeAmt) -> &Self,
  |                                      ^^^^ `Self` is only available in traits and impls

error[E0106]: missing lifetime specifier
 --> shal_proto/src/unit_tests.rs:7:37
  |
7 |             set_brakes(BrakeAmt) -> &Self,
  |                                     ^ expected lifetime parameter

error: aborting due to 2 previous errors

I tried:

mock_trait!(ChalMock,
            set_brakes(BrakeAmt) -> &ChalMock,
            brakes() -> BrakeAmt);

impl Chal for ChalMock {
    mock_method!(set_mech_brakes(&self, amt: BrakeAmt) -> &ChalMock);
    mock_method!(mech_brakes(&self) -> BrakeAmt);
}

which helps, but I am still left with:

error[E0106]: missing lifetime specifier
 --> shal_proto/src/unit_tests.rs:7:37
  |
7 |             set_brakes(BrakeAmt) -> &ChalMock,
  |                                     ^ expected lifetime parameter

error: aborting due to previous error

How can I make a mock of Chal using double?

@DonaldWhyte
Copy link
Owner

Thanks for pointing this out!

Unfortunately, Rust's hygenic macros prevent us from being able to mock traits/methods which use the Self type, since the compiler can't deduce the meaning Self when it's used in a macro. I'll properly document that limitation and create a GitHub issue to track research into resolving it.

As for the second compiler error, I don't have an immediate explanation/solution. I will look into this tomorrow evening (UTC) and update this issue with whatever I find.

@U007D
Copy link
Author

U007D commented Dec 13, 2017

Thanks for the reply, @DonaldWhyte!

The Self issue seems to be something I can work around simply by supplying the type explicitly as I have done above.

For the lifetime issue, the compiler doesn't know that the lifetime of the reference being returned is tied to that of the (implied) &self argument, and because that argument is implied, there doesn't seem to be a way to explain this relationship to the compiler. (The practice of returning &Self is common when creating fluent API's.)

Hopefully some kind of workaround will spring to mind for the lifetimes issue! Fingers crossed... :)

@U007D
Copy link
Author

U007D commented Dec 15, 2017

Hi, @DonaldWhyte. Checking in to see if there is any news before I try crafting a home-grown workaround?

@DonaldWhyte
Copy link
Owner

DonaldWhyte commented Jan 21, 2018

Hey @U007D! I haven't forgotten about this. I've been spending most of the time I have available on double on implementing a concise/expressive call pattern matching API (inspired by Google Mock's).

That'll land into master and be deployed within the next couple of days, so I can begin to look at this again.

Recapping the two issues, we have:

  1. cannot use Self in traits you wish to mock
  2. Not being able to mock functions that return references

Unfortunately, I think solving (1) is out of reach at the time of writing. I believe Rust's hygenic macros are not sophisticated enough for this. There's a probably a hacky workaround that half solves the problem, but I think solving (2) is a higher priority.

(2) is a big problem. Currently, any functions that return references cannot be mocked. My proposal for this is for the generated mock (there's one Mock object per trait method) to be aware that it returns a reference. Mock already owns the values it's configured to return. It can simply return a borrowed reference to the configured return value it owns.

There are some subtleties and corner cases to handle. These are the cases where Mocks return values are constructed by functions or closures. The value is not known in advance and is thus, not owned by the Mock. This can be handled though. I'll spec out a prototype PR sometime in the next 1-3 weeks.

@U007D
Copy link
Author

U007D commented Jan 21, 2018

Thank you for the update, @DonaldWhyte! Yes, those are the two main issues.
I do hope the Self issue is solvable, but I agree with you in that not being able to return refs is probably the better one to solve first.

I look forward to the PR once you have it ready! Thanks again for the update.

@DonaldWhyte DonaldWhyte self-assigned this Jan 27, 2018
@kaiba696
Copy link

kaiba696 commented Jun 12, 2018

+1 on this.. also curious about supporting mocking for traits with associated types (https://doc.rust-lang.org/book/second-edition/ch19-03-advanced-traits.html)

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

3 participants