Infinite borrowing when passing borrowed value to Box<dyn Trait>

This top part is mainly useful if you like solving lifetime puzzles. Something potentially useful is at the bottom.

The low-level cause (which does prevent this high-level unsoundness) is invariance. Namely, trait parameters are invariant. So in the case of a trait object, the method signature takes

``````&dyn MyTrait<Chars<'c>>, Chars<'c>
//           ^^^^^^^^^
``````

Because the underlined part is invariant, the lifetime has to be the entirety of the borrow `'c`. This is what ties the borrow of `s` to the type of `dyn_trait`:

``````Box<dyn MyTrait<Chars<'c>> + '_>
``````

and the final piece of the puzzle is that trait objects always have a non-trivial drop, so from a low-level borrow checking perspective, there's a use of the borrow of `s` when `dyn_trait` goes out of scope.

More generally, if the following three ingredients are a recipe for borrow entanglement:

1. A method taking different parameters with the same lifetime
2. Something causing the lifetime to be invariant
3. A non-trivial drop

The invariance of trait parameters means that a `dyn MyTrait<Chars<'c>>` is saying, "I can only work with `Chars<'a>` if `'a` is exactly `'c`". Contravariance would be "`'c` or longer", etc.

There is a way to be more general without changing the trait: you can have a higher-ranked bound that says "I can work with `Chars<'a>` for any `'a`". That would be this change:

``````-    let dyn_trait: Box<dyn MyTrait<_>> = Box::new(X {});
+    let dyn_trait: Box<dyn for<'a> MyTrait<Chars<'a>>> = Box::new(X {});
``````

Now the lifetimes in the method call don't have to be the same.

Whether the implementors you want can meet that bound or not is a different question, so this may or may not be a viable alternative.

In terms of @2e71828 solution, you will end up with the same borrowing situation as the OP if the iterator `Item` type is something borrowed. The higher-ranked trait object type (commented in the playground) again may or may not be a viable workaround, depending on the `MyTrait` implemention of the base type.

2 Likes