impl Trait cannot work well with lifetime bounds
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.
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
}
}
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.
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. :])
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> {
| +++++++++++++
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?