2

I'm trying to hide nix::dir::{Dir, Entry, Iter} behind a trait so that I can fuzz test my app without scrambling my real file system by providing a mock implementation of that trait.

As a simplified model of the problem, I wrote this:

#[derive(Debug)]
pub struct Source(u8);
// this is similar to nix::dir::Dir;
#[derive(Debug)]
pub struct Ref<'a>(&'a mut Source, usize);
// this is similar to nix::dir::Iter
// which implements Iterator<Item = nix::Result<nix::dir::Entry>>

trait Foo<T, R> {
    fn borrow(&self, source: &mut T) -> R;
}

impl Foo<Source, Ref<'_>> for () {
    fn borrow<'a>(&self, source: &'a mut Source) -> Ref<'a> {
        Ref(source, 0)
    }
}

fn main() {
    println!("{:#?}", ().borrow(&mut Source(13)));
}

When I try to compile this with cargo run --bin sandbox on a stable Rust, I get this error:

error: `impl` item signature doesn't match `trait` item signature
  --> src/sandbox.rs:14:5
   |
10 |     fn borrow(&self, source: &mut T) -> R;
   |     -------------------------------------- expected fn(&(), &mut Source) -> Ref<'_>
...
14 |     fn borrow<'a>(&self, source: &'a mut Source) -> Ref<'a> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&(), &mut Source) -> Ref<'_>
   |
   = note: expected `fn(&(), &mut Source) -> Ref<'_>`
              found `fn(&(), &mut Source) -> Ref<'_>`

The "expected" and "found" parts look equal to me. What's going on?

Where (if anywhere) do I put the lifetime parameters such that I can return a new object containing a mutable reference to an argument from a trait method, when that object's type is parameterized by a lifetime?

I guess instead of returning an iterator I could have my trait implementations be responsible for doing the iteration and returning a vector, or I can take a callback, or whatever—I'm confident I can solve my practical problem, but I would love to understand Rust a little better.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Jonas Kölker
  • 7,680
  • 3
  • 44
  • 51
  • 1
    The short, unexplained answer is that the first `'_` is not compatible with the second `'_`, so the `Ref<'a>` is not acceptable for the `Ref<'_>` defined in the impl signature. They are not the same. I believe that you need generic associated types to represent this in practice. – E_net4 Apr 15 '20 at 16:16
  • Are you looking for https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7df107296130910d0fd4a64921ce052d ? – Shepmaster Apr 15 '20 at 16:33
  • It looks like your question might be answered by the answers of [What does it mean for a trait to have a lifetime parameter?](https://stackoverflow.com/q/29975854/155423); [Why does my trait need a lifetime parameter?](https://stackoverflow.com/q/36117966/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Apr 15 '20 at 16:33
  • @Shepmaster your playground _might_ be what I'm looking for. I looked at the questions you linked to, and they kinda' shine light in the general direction I'd like to have illuminated. Could you explain (or point to docs which explain) the implications of your choice of parameterizing some but not other types and/or lifetimes? What are the rules of Rust that makes your solution work, and what does 'work' mean, exactly? – Jonas Kölker Apr 15 '20 at 17:02
  • Do you mean [When is it appropriate to use an associated type versus a generic type?](https://stackoverflow.com/q/32059370/155423)? I don't know that it *matters* here, [your original form works](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=73b1ecb1bb8b43c0e637f45fcd796406) like that too. The associated type is just more common. – Shepmaster Apr 15 '20 at 17:04
  • Thanks for the reference to the associated type, I definitely want that. Back to trait lifetime parameters: you wrote in "What does it mean..." that in `trait T<'a>`, the implementers of `T` can store references with `'a` lifetimes. Is returning them (or objects containing them) "the same as" storing (for some value of ...)? I think of generic parameters as universal quantifiers; with `trait T<'a> { fn f() -> U<'a> }`, can I consume multiple different `f` return values in multiple different lifetimes? But it's the same `'a', bound once at the trait level? I must be misconceiving something?! – Jonas Kölker Apr 15 '20 at 17:54
  • With a type parameter `'a` in the trait, you are right that the lifetimes of the respective `&Source` and `Ref` will need to be compatible with that lifetime. As a function argument or return value, they need to be covariant with respect to that lifetime. There is a deeper explanation on this [here](https://stackoverflow.com/a/42639814/1233251). – E_net4 Apr 16 '20 at 09:35
  • I see. So is it possible to have a trait method `foo(self, other)` such that the the return value is a struct parameterized by a lifetime, and that lifetime is coupled to (must outlive) `other` but not `self`? – Jonas Kölker Apr 17 '20 at 19:11

0 Answers0