asomers / mockall

A powerful mock object library for Rust
Apache License 2.0
1.5k stars 62 forks source link

Lifetime issue with `&[&str]` argument #422

Closed WilliamVenner closed 1 year ago

WilliamVenner commented 1 year ago

First, this doesn't work due to missing lifetime specifier:

use mockall::{mock, predicate::*};

trait Something {
    fn method(&self, stuff: &[&str]);
}

mock! {
    pub Client {}
    impl Something for Client {
        fn method(&self, stuff: &[&str]);
    }
}

error[E0106]: missing lifetime specifier
  --> src/lib.rs:10:35
   |
10 |         fn method(&self, stuff: &[&str]);
   |                                   ^ expected named lifetime parameter
   |
   = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider making the bound lifetime-generic with a new `'a` lifetime
   |
7  ~ for<'a> mock! {
8  |     pub Client {}
9  |     impl Something for Client {
10 ~         fn method(&self, stuff: &[&'a str]);
   |
help: consider introducing a named lifetime parameter
   |
10 ~         fn method(&self, stuff: &[&'a str]);
11 |     }
12 ~ }<'a>
   |

error[E0637]: `&` without an explicit lifetime name cannot be used here
  --> src/lib.rs:10:35
   |
10 |         fn method(&self, stuff: &[&str]);
   |                                   ^ explicit lifetime name needed here

Second, I can't seem to get this to work at all, even with a specified lifetime parameter:

use mockall::{mock, predicate::*};

trait Something {
    fn method<'a>(&self, arg: &str, stuff: &[&'a str]);
}

mock! {
    pub Client {}
    impl Something for Client {
        fn method<'a>(&self, arg: &str, stuff: &[&'a str]);
    }
}

fn mocking() {
    let mut mock = MockClient::new();
    mock.expect_method()
        .with(eq("foo"), function(move |stuff: &[&str]| true))
        .return_const(());
}

error: implementation of `Predicate` is not general enough
  --> src/lib.rs:16:5
   |
16 | /     mock.expect_method()
17 | |         .with(eq("foo"), function(move |stuff: &[&str]| true))
   | |______________________________________________________________^ implementation of `Predicate` is not general enough
   |
   = note: `predicates::function::FnPredicate<[closure@src/lib.rs:17:35: 17:56], [&'2 str]>` must implement `Predicate<[&'1 str]>`, for any lifetime `'1`...
   = note: ...but it actually implements `Predicate<[&'2 str]>`, for some specific lifetime `'2`

How can I get it to play nice?

WilliamVenner commented 1 year ago

I noticed using withf has relaxed lifetime bounds -

  1. Does this result in poorer output of test error messages, when there are more than 1 parameters to evaluate?
  2. Can the lifetime bounds on with not be similarly relaxed in order to accommodate this?
asomers commented 1 year ago

I noticed using withf has relaxed lifetime bounds -

1. Does this result in poorer output of test error messages, when there are more than 1 parameters to evaluate?

Yes, exactly. Using a predicate like eq gives better error messages than withf.

2. Can the lifetime bounds on `with` not be similarly relaxed in order to accommodate this?

Nope. The predicates passed to with must be 'static. In your case, you may be able to do function(move for<'a> |stuff: &[&'a str]| true).

WilliamVenner commented 1 year ago

I did actually try your suggestion (it requires nightly atm) and it didn't fix it unfortunately.

asomers commented 1 year ago

Then your best bet is to use withf.

asomers commented 1 year ago

I'm going to close this, on the assumption that I've answered your question.