mockall
mockall copied to clipboard
Unexpected `undeclared lifetime` error
I have not been able to compile the following mock (neither with mock! nor automock):
mockall::mock! {
TestAccountsReportWriter {}
#[async_trait(?Send)]
impl AccountsReportWriter for TestAccountsReportWriter {
async fn write_accounts_report<'a, T>(&'a mut self, report: T) -> anyhow::Result<()>
where
T: Iterator<Item = AccountReport> + 'a;
}
}
The error I see is:
error[E0261]: use of undeclared lifetime name `'a`
--> src/processors/simple.rs:87:45
|
85 | async fn write_accounts_report<'a, T>(&'a mut self, report: T) -> anyhow::Result<()>
| - help: consider introducing lifetime `'a` here: `'a,`
86 | where
87 | T: Iterator<Item = AccountReport> + 'a;
| ^^ undeclared lifetime
error[E0261]: use of undeclared lifetime name `'a`
--> src/processors/simple.rs:87:45
|
85 | async fn write_accounts_report<'a, T>(&'a mut self, report: T) -> anyhow::Result<()>
| - help: consider introducing lifetime `'a` here: `'a,`
86 | where
87 | T: Iterator<Item = AccountReport> + 'a;
| ^^ undeclared lifetime
88 | }
89 | }
| - lifetime `'a` is missing in item created through this procedural macro
I've tested both versions 0.9.1 and 0.10.0 (using rustc 1.53.0)
It seems like the macro misses to generate the lifetime 'a, and then the compiler complains about it being used in the where clause, but it doesn't complain about its use in other parts of the signature.
I'm pretty new to this library and I don't know where else to look at. Please let me know if you need further information to help with the identification of the problem. Thanks a lot ;-)
I guess this may be related to #293. I've been seeing what's generated by using cargo expand. I've tested this with a simpler trait:
#[mockall::automock]
pub trait MyTrait {
fn do_something<'a>(&'a self, report: &'a String) -> Result<(), String>;
}
This works perfectly.
On the other hand, if we introduce a new generic parameter, by changing report: &'a String for report: &'a T:
#[mockall::automock]
pub trait MyTrait {
fn do_something<'a, T>(&'a self, report: &'a T) -> Result<(), String>;
}
We get an error. And the code being generated for generics doesn't take the lifetimes into account:
impl GenericExpectations {
/// Simulating calling the real method.
pub fn call<T>(&self, report: &'a T) -> Option<Result<(), String>> {
There are some limitations when mocking generic methods. First, Mockall doesn't currently doesn't support generic methods that have both generic type parameters and generic lifetime parameters. That could probably be fixed. However, your bigger problem is that Mockall doesn't support generic methods with non-'static generic type arguments. That's a limitation in the language that Mockall can't work around. If you're a language expert, you might chime in at https://internals.rust-lang.org/t/hrtbs-for-unspecified-lifetimes/14868 .
I suggest two work arounds:
- Bound
Tby'static, at least during#[cfg(test)]. - Manually implement the trait on the mock object in terms of a method that can be mocked. For example, you could unsafely transmute the lifetime to static, making the mock method take only static generic type arguments.
https://docs.rs/mockall/0.10.1/mockall/#methods-with-generic-lifetimes
Mockall doesn't currently doesn't support generic methods that have both generic type parameters and generic lifetime parameters. That could probably be fixed.
Is there a ticket or rough estimate of effort to enable this? Curious because I've started integrating mockall into a proof of concept and have a feeling I may run into this.
No ticket for it. And no effort estimate. Sorry.
I'm not sure if this is helpful or not, but another small example that reproduces this issue:
struct Ref<'a>(&'a ());
#[mockall::automock]
trait Foo {
fn bar(&self, x: Ref<'_>) -> Ref<'_>;
// ~~ error: `'_` cannot be used here
fn baz<'a>(&'a self, x: Ref<'a>) -> Ref<'a>;
// ~~ error: use of undeclared lifetime name `'a`
fn baw<'a>(&'a self, x: Ref<'a>); // ok.
}
Note that this does not involve any generic types, only generic lifetimes.
I also ran into this problem mocking a production service, specifically a trait that returned a boxed Stream with a lifetime parameter.