rust icon indicating copy to clipboard operation
rust copied to clipboard

impl Trait cannot work well with lifetime bounds

Open oxalica opened this issue 6 years ago • 3 comments

Playground

pub struct S<'t>(usize, &'t str);

impl<'t> S<'t> {
    // error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
    pub fn foo<'s>(&'s mut self) -> impl FnMut() + 's {
        move || self.0 += 1
    }
}

fn main() {
    let mut s = S(0, "hello");
    s.foo()();
    println!("{}", s.0);
}

The self: &'s mut S<'t> implies 't: 's, so the signature should be correct. But compiler still complain that 't is captured but does not appear in impl bounds.

I checked the error description of E0700 and tried impl FnMut() + 's where 't: 's and impl FnMut() + 's + 't, while it still fails.

Note that when using dyn Trait as pub fn foo<'s>(&'s mut self) -> Box<dyn FnMut() + 's>, it compiles.

oxalica avatar Nov 19 '19 19:11 oxalica

You could workaround using the Captures trait.

pub trait Captures<U: ?Sized> {}
impl<U: ?Sized, T: ?Sized> Captures<U> for T {}

impl<'t> S<'t> {
    pub fn foo(&mut self) -> impl FnMut() + Captures<&'t str> + '_ {
        move || self.0 += 1
    }
}

kennytm avatar May 17 '20 15:05 kennytm

Could the Captures trait make it into std so that rustc can suggest it? It's otherwise impossible to discover this trick but it's a very useful one.

Nadrieril avatar Dec 17 '20 23:12 Nadrieril

The Captures trick is a builtin syntax use<..> in 1.82: https://blog.rust-lang.org/2024/10/17/Rust-1.82.0.html:

pub fn foo<'s>(&'s mut self) -> impl FnMut() + use<'s, 't> {
    move || self.0 += 1
}

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

And I think even that much gets erased in the 2024 edition, where it's implicit.

(I'm coming back to this old bug because my use of the Captures trait linked back to this issue, and I am joyfully replacing that usage. :])

ssbr avatar Oct 18 '24 06:10 ssbr

Current output:

error[E0700]: hidden type for `impl FnMut() + 's` captures lifetime that does not appear in bounds
 --> src/main.rs:5:9
  |
3 | impl<'t> S<'t> {
  |      -- hidden type `{closure@src/main.rs:5:9: 5:16}` captures the lifetime `'t` as defined here
4 |     pub fn foo<'s>(&'s mut self) -> impl FnMut() + 's {
  |                                     ----------------- opaque type defined here
5 |         move || self.0 += 1
  |         ^^^^^^^^^^^^^^^^^^^
  |
help: add a `use<...>` bound to explicitly capture `'t`
  |
4 |     pub fn foo<'s>(&'s mut self) -> impl FnMut() + 's + use<'s, 't> {
  |                                                       +++++++++++++

Dylan-DPC avatar Jan 03 '25 15:01 Dylan-DPC

I've been using the Captures<> hack (https://github.com/rust-lang/rust/issues/66551#issuecomment-629815002) since a long time ago, but since use<> has been a part of the language now, I guess the original problem has been resolved, and we should close this issue?

rami3l avatar Jan 04 '25 01:01 rami3l