2

I have a generic function creating a local object and taking a trait specifying what to do with that object. The trait takes the reference to the object and holds it for it's lifetime (to avoid passing it again and again to every function call). It dies before the

fn do_stuff<'a, T>()
  where T : BigBorrower<'a>
{
  let borrowee = Borrowed{ data : 1 };
  {
    let _borrowee = T::new(&borrowee);
  }
}

This is the function call. Because the lifetime for trait has to be specified in function declaraion, it makes the compiler think the lifetime extends lifetime of _borrowee.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a445fb4ab7befefbadd3bdb8fb43c86a

   |
24 | fn do_stuff<'a, T>()
   |             -- lifetime `'a` defined here
...
29 |     let _borrowee = T::new(&borrowee);
   |                     -------^^^^^^^^^-
   |                     |      |
   |                     |      borrowed value does not live long enough
   |                     argument requires that `borrowee` is borrowed for `'a`
30 |   }
31 | }
   | - `borrowee` dropped here while still borrowed
  • The immediate problem resembles [How do I write the lifetimes for references in a type constraint when one of them is a local reference?](https://stackoverflow.com/q/44343166/3650362) Putting an HRTB on `T` alerts you that `SomeBigBorrower<'_>` doesn't satisfy the constraint. You can use [a "family" trait](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8e3e4d90545045ddf28cb4b796729abf) to make it work by deferring the choice of `'a` to inside `do_stuff` (but you probably should look for another solution). – trent Sep 05 '19 at 15:08

1 Answers1

3

You've just hit one of the issues with lifetimes and the compiler. Once you realize why it happens, it makes sense.

Your method call enforces a lifetime 'a for the generic type you're providing. This means, amongst other things, that this lifetime needs to be respected and that all objects are required to live for that long. In practice, when you are doing that, the lifetime is that of the function call.

By passing T::new() a reference to a local variable, you are forcing the compiler to pick a lifetime that is inferior to 'a (since it will not outlive the function call), and thus, you are going against your own requirements.

Typically, the way you solve this is to split your do_stuff<'a, T> into two, like on this playground sample. This makes the lifetime check palatable by the compiler, seeing as the life expectancy of that reference is guaranteed to be longer than that of the function being called.

Do note that I renamed your method new in the trait and implementations to borrow, as that's closer to what it is.

trent
  • 25,033
  • 7
  • 51
  • 90
Sébastien Renauld
  • 19,203
  • 2
  • 46
  • 66
  • Is there a way to create the object in a proxy function with a Trait? Ideally I would like to hide the fact of creating the intermediate object as it's an implementation detail, I only want to pass the trait to the first function. Now that I understand the problem better, isn't that the same issue as https://stackoverflow.com/questions/28029565/confused-about-using-trait-with-lifetime-as-generic-parameter-constraint? Basically I can't pass the trait without lifetime constraint... – user2312937 Sep 04 '19 at 22:42
  • That is indeed the same problem, and also highlights the fact that it just cannot be done directly right now. There is simply no way to express this lifetime requirement in a way that satisfies the conditions right now. One question - on the creation of the object - is the root object (`Borrowed` in your case) shared in any way, or copied? If not, you might as well get `Borrowee` to take ownership of it. – Sébastien Renauld Sep 05 '19 at 08:54