1

I have two structures, one of which has a type parameter that is set to a reference to another. When I take that reference as an argument in a function, I don't know how to signal that the lifetime should not become dependent.

I've trimmed it down to this example:

use std::marker::PhantomData;

struct Foo<T> {
    bar_type: PhantomData<T>,
}

struct Bar {}

impl<T> Foo<T> {
    fn takebar(&mut self, bar: T) {}
}

fn main() {
    let mut foo: Foo<&Bar> = Foo {
        bar_type: PhantomData,
    };
    let bar = Bar {};

    foo.takebar(&bar);
}

This gives me the following compiler error:

error[E0597]: `bar` does not live long enough
  --> src/main.rs:19:18
   |
19 |     foo.takebar(&bar);
   |                  ^^^ borrowed value does not live long enough
20 | }
   | - `bar` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

I understand that foo needs to live as long as bar here, so I can "resolve" this by declaring bar before foo to reverse their destruction order.

This won't work for my situation, and more importantly, I'm not sure why it's happening or how to avoid it. I've tried all sorts of explicit lifetime setups and none of them seem to resolve the problem. If I turn takebar(&mut self, ... into takebar(&self, ..., it also works, but I can't always do that. It also works if I move the type parameter to the function from the struct, but again, can't always do that.

How do I communicate that the &bar reference should not be tied into foo's lifetime, and only actually needs to live through the function call?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Taywee
  • 1,313
  • 11
  • 17
  • 1
    [This users.rust-lang.org post](https://users.rust-lang.org/t/expressing-hrtb-like-bound-on-generic-struct/18081) is remarkably similar. – trent Jun 15 '18 at 17:58
  • @trentcl That is almost exactly my issue (in my situation, it's essentially a serializing map, which serializes values inbound and deserializes them again on retrieval, so pretty much the same in a lot of ways). I just didn't know how to find it. That's incredibly useful information, thank you. – Taywee Jun 15 '18 at 18:01

1 Answers1

2

You cannot.

A simpler reproduction:

struct Foo<T>(Option<T>);
struct Bar;

impl<T> Foo<T> {
    fn takebar(&mut self, bar: T) {}
}

fn main() {
    let mut foo: Foo<&Bar> = Foo(None);
    let bar = Bar;

    foo.takebar(&bar);
}

The compiler only cares about the function signature. One possible implementation of takebar with the existing function signature is one which stores it:

fn takebar(&mut self, bar: T) {
    self.0 = Some(bar)
}

Thus, the example code must be prevented from compiling.

Normally, I'd advocate for putting another type parameter on the function and expressing the operations that you need to do, but you've already ruled that out:

fn takebar<T2>(&mut self, bar: T2)
where
    T2: PartialEq<T>,
{
    self.0.take();
}

If I turn takebar(&mut self, ... into takebar(&self, ..., it also works

Yes, because you cannot store the value then (unless you add interior mutability, in which case the error comes back).

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Hmm. I thought I might be able to use explicit lifetimes or something of the sort to tell the compiler that bar needs to be the same type, but doesn't need to live as long because no references to it are kept. If that's impossible, I suppose I just need to try to find a better way to do it. Thank you. – Taywee Jun 15 '18 at 17:59
  • 1
    @Taywee the lifetime is *part of the type*, they aren't distinct. – Shepmaster Jun 15 '18 at 18:31
  • Ah, there we go, that makes it really click. Thank you so much for that. I'll see if I can refactor this to make it work. – Taywee Jun 15 '18 at 18:58