Description
I tried this code:
use std::marker::PhantomData;
/// Some data. In real life, imagine that its content is arena-allocated with lifetime `'a`.
struct Data<'a> {
_marker: PhantomData<&'a ()>,
}
struct DataAlloc;
/// Data with borrowed content that can be copied from one location to another.
trait CopyTo {
/// This is always `Self`, be we need associated types to make Rust understand that `Self` is
/// actually parametric over some lifetime.
type Data<'a>;
/// Copy data from the current allocator to `dest`.
fn copy_to<'from, 'to>(data: Self::Data<'from>, dest: &'to DataAlloc) -> Self::Data<'to>;
}
impl CopyTo for Data<'_> {
type Data<'a> = Data<'a>;
fn copy_to<'from, 'to>(data: Data<'from>, _dest: &'to DataAlloc) -> Data<'to> {
Data {
_marker: PhantomData,
}
}
}
impl DataAlloc {
pub fn copy<'from, 'to, T: CopyTo>(&'to self, data: T::Data<'from>) -> T::Data<'to>
where
T: CopyTo<Data<'to>: Sized>,
{
T::copy_to(data, self)
}
}
I expected to see this happen: this code should compile. In fact, if I remove the where T: CopyTo<Data<'to>: Sized>
clause, which isn't used at all, this code compiles.
Instead, this happened: I get a lifetime may not live long enough error, with the note "Proving this value is Sized
requires to to
must outlive 'from
" as well as the dual message:
error: lifetime may not live long enough
--> src/lib.rs:35:20
|
31 | pub fn copy<'from, 'to, T: CopyTo>(&'to self, data: T::Data<'from>) -> T::Data<'to>
| ----- --- lifetime `'to` defined here
| |
| lifetime `'from` defined here
...
35 | T::copy_to(data, self)
| ^^^^ proving this value is `Sized` requires that `'to` must outlive `'from`
|
= help: consider adding the following bound: `'to: 'from`
error: lifetime may not live long enough
--> src/lib.rs:35:20
|
31 | pub fn copy<'from, 'to, T: CopyTo>(&'to self, data: T::Data<'from>) -> T::Data<'to>
| ----- --- lifetime `'to` defined here
| |
| lifetime `'from` defined here
...
35 | T::copy_to(data, self)
| ^^^^ proving this value is `Sized` requires that `'from` must outlive `'to`
|
= help: consider adding the following bound: `'from: 'to`
Context
This is a simplified version of a real use-case where I have arena-allocated data and I want a trait that implements moving them from one arena to another with a different lifetime. Since Rust doesn't support general higher-kinded types, I use GATs as a poor man's HKT, which requires more annotations but it works ok. However, when I try to implement a variant of copy
where I need additional bounds on the associated type, I started to get strange lifetime errors. What's surprising here is that the trait bound is unrelated to the body of the function and never used. It's surprising that adding a Sized
bound adds any lifetime constraints at all, unless I'm missing something obvious.
Potential duplicate of #108345, but I wanted to make sure it's not a different error first.
Meta
rustc --version --verbose
:
rustc 1.84.1 (e71f9a9a9 2025-01-27)
binary: rustc
commit-hash: e71f9a9a98b0faf423844bf0ba7438f29dc27d58
commit-date: 2025-01-27
host: x86_64-unknown-linux-gnu
release: 1.84.1
LLVM version: 19.1.5