Returning &Self
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?
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.
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... :)
Hi, @DonaldWhyte. Checking in to see if there is any news before I try crafting a home-grown workaround?
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:
- cannot use
Selfin traits you wish to mock - 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.
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.
+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)