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

Unexpected undeclared lifetime error #299

Open
chris-zen opened this issue Jun 27, 2021 · 6 comments
Open

Unexpected undeclared lifetime error #299

chris-zen opened this issue Jun 27, 2021 · 6 comments
Labels
question Usage question

Comments

@chris-zen
Copy link

chris-zen commented Jun 27, 2021

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 ;-)

@robertohuertasm
Copy link

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>> {

@asomers
Copy link
Owner

asomers commented Jul 4, 2021

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 T by '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

@asomers asomers added the question Usage question label Jul 4, 2021
@mattremmel
Copy link

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.

@asomers
Copy link
Owner

asomers commented Jul 21, 2021

No ticket for it. And no effort estimate. Sorry.

@nlordell
Copy link

nlordell commented Dec 3, 2021

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.

@kellpossible
Copy link

kellpossible commented Jul 26, 2022

I also ran into this problem mocking a production service, specifically a trait that returned a boxed Stream with a lifetime parameter.

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

No branches or pull requests

6 participants