-
-
Notifications
You must be signed in to change notification settings - Fork 14.5k
Description
Here's an interesting issue I ran into when writing code that involves GATs.
trait Provide {
type State<'a>
where
Self: 'a;
fn provide(&self) -> Self::State<'_>;
}
trait Consume {
fn consume(self);
}
// Holds T, but needs to create T::State in its ctor.
struct Holder<T>(T);
impl<T> Holder<T> {
fn use_and_hold<'a>(val: T) -> Self
where
T: Provide + 'a,
T::State<'a>: Consume,
{
let state = val.provide();
// drop(state);
state.consume();
Holder(val)
}
}Which emits two errors
- One saying that
valdoes not live long enough for the call tostate.consume(), - and another saying that it cannot move out of
valatHolder(val)because it is still borrowed.
The whole diagnostic is included further down.
If you comment out the line with state.consume(); and instead add drop(state); it compiles. As far as I know, these should behave exactly the same as the free drop function is nothing special. The only difference I could see was the T: Destruct bound on drop in rust-analyzer (1.95 nightly, see below), but this can hardly be it, can it?
Somewhat tangentially: While minimizing this from my actual code, I didn't have the T: 'a bound initially. I noticed that it was needed only after I removed an associated type from Consume which was used in the return type of use_and_hold. The additional diagnostic that is shown when you remove the T: 'a bound is not emitted when the return type of use_and_hold more complex. I don't know if this is directly related to this problem, but here's an example of it.
Meta
I initially ran into this on 1.89 stable:
rustc 1.89.0 (29483883e 2025-08-04)
binary: rustc
commit-hash: 29483883eed69d5fb4db01964cdf2af4d86e9cb2
commit-date: 2025-08-04
host: x86_64-unknown-linux-gnu
release: 1.89.0
LLVM version: 20.1.7
I've also tested this with 1.95 nightly:
rustc 1.95.0-nightly (f134bbc78 2026-01-24)
binary: rustc
commit-hash: f134bbc78dac04a17324341a8d4a15a6930d81a7
commit-date: 2026-01-24
host: x86_64-unknown-linux-gnu
release: 1.95.0-nightly
LLVM version: 21.1.8
Complete Diagnostic
❯ cargo c
Checking foo v0.1.0 (/home/tinger/foo)
error[E0597]: `val` does not live long enough
--> src/lib.rs:23:21
|
18 | fn use_and_hold<'a>(val: T) -> Self
| -- --- binding `val` declared here
| |
| lifetime `'a` defined here
...
23 | let state = val.provide();
| ^^^ borrowed value does not live long enough
24 | // drop(state);
25 | state.consume();
| --------------- argument requires that `val` is borrowed for `'a`
...
28 | }
| - `val` dropped here while still borrowed
error[E0505]: cannot move out of `val` because it is borrowed
--> src/lib.rs:27:16
|
18 | fn use_and_hold<'a>(val: T) -> Self
| -- --- binding `val` declared here
| |
| lifetime `'a` defined here
...
23 | let state = val.provide();
| --- borrow of `val` occurs here
24 | // drop(state);
25 | state.consume();
| --------------- argument requires that `val` is borrowed for `'a`
26 |
27 | Holder(val)
| ^^^ move out of `val` occurs here
|
help: if `T` implemented `Clone`, you could clone the value
--> src/lib.rs:17:6
|
17 | impl<T> Holder<T> {
| ^ consider constraining this type parameter with `Clone`
...
23 | let state = val.provide();
| --- you could clone this value
Some errors have detailed explanations: E0505, E0597.
For more information about an error, try `rustc --explain E0505`.
error: could not compile `foo` (lib) due to 2 previous errors
The line numbers may be off, I added #![allow(dead_code)] locally.