Lifetime error on mere presence of struct member (even if not used)

I'm very intrigued by the error below. Somehow, the present of coefficients: Box<dyn M<'a, T, I> + 'a>, makes it impossible to borrow with as_my_iterator, but if you comment it out, it works. I truly have no idea why the mere presence of such member makes this error, but if you comment this line, you see that everything works.

use std::marker::PhantomData;

impl<'a, T, I> std::fmt::Debug for MyIterable<'a, T, I> {
	fn fmt(
		&self,
		f: &mut std::fmt::Formatter<'_>,
	) -> std::fmt::Result {
		let x = self.as_my_iterator();
		Ok(())
    }
}

pub trait M<'r, T: 'r, I>
{
}

pub struct MyIterable<'a, T, I> {
    //Comment this line:
	coefficients:   Box<dyn M<'a, T, I> + 'a>,
	_phantom1: PhantomData<&'a T>,
	_phantom2: PhantomData<&'a I>
}

pub struct MyIterator<'a, T> {
	pub(crate) coefficients:   &'a [T],
}

pub trait AsMyIterator<'a, T> {
	fn as_my_iterator(&'a self) -> Result<MyIterator<'a, T>, ()>;
}

impl<'a, T, I> AsMyIterator<'a, T> for MyIterable<'a, T, I> {
	fn as_my_iterator(
		&'a self
	) -> Result<
		MyIterator<'a, T>,
		(),
	> {
		todo!();
	}
}

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

Error:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
 --> src/lib.rs:8:16
  |
8 |         let x = self.as_my_iterator();
  |                      ^^^^^^^^^^^^^^
  |
note: first, the lifetime cannot outlive the anonymous lifetime defined here...

What is happening?

Hmm, we have an even more weird error message when we do the following:

impl<T, I> std::fmt::Debug for MyIterable<'_, T, I> {
	fn fmt(
		&self,
		f: &mut std::fmt::Formatter<'_>,
	) -> std::fmt::Result {
		let x = self.as_my_iterator();
		Ok(())
    }
}

(Rust Playground)

Truncated message:

  |
8 |         let x = self.as_my_iterator();
  |                      ^^^^^^^^^^^^^^
  = note: expected `<MyIterable<'_, T, I> as AsMyIterator<'_, T>>`
             found `<MyIterable<'_, T, I> as AsMyIterator<'_, T>>`

This is due to variance. The type MyIterable<'a, T, I> is either covariant or invariant in the lifetime argument 'a. If it’s covariant, then the &self argument of the Debug for MyIterable<'a, T, I> implementation, which is of type &'b MyIterable<'a, T, I> (where 'b is supposed to stand for the elided lifetime of the &self argument) can be turned into &'b MyIterable<'b, T, I> which fits the requirements of <MyIterable<'b, T, I> as AsMyIterator<'b, T>>::as_my_iterator, i.e. the requirement to get an argument of type &'b MyIterable<'b, T, I> with the lifetimes of the reference and the MyIterable parameter matching.

If it’s invariant, then no conversion of &'b MyIterable<'a, T, I> into &'a MyIterable<'a, T, I> can happen.

The type dyn M<'a, T, I> + … is invariant with respect to 'a, so introducing a Box<dyn M<'a, T, I> + 'a> field makes a previously covariant lifetime argument 'a be invariant instead.


It’s often a mistake to require arguments like the &'a self argument in

impl<'a, T, I> AsMyIterator<'a, T> for MyIterable<'a, T, I> {
	fn as_my_iterator(
		&'a self
	) -> Result<
		MyIterator<'a, T>,
		(),
	> {
		todo!();
	}
}

that require two lifetime parameters on different levels of nesting to be the same. The lifetime of the reference &'a self is the same lifetime 'a as in MyIterable<'a, T, I>; this decision makes the API inflexible to use. If it’s possible to generalize this implementation (or perhaps even change the trait) in a way to avoid that, that’s your best way to fix the issue. I don’t know how possible that is in your case, as the code you provide here seems to be either fabricated, or a simplified version of a real issue.

2 Likes

Is the confusing error message a known issue?

Yeah, it’s generally known that lifetime errors are hard to read if they talk about lots of elided lifetimes; those lifetimes don’t have a name, and the compiler doesn’t always come up with stand-in names for you. Also the issue that sometimes a note: expected … found … prints two types that look the same, is known in principle.

But I’m not actually sure why the error messages talk about something of the form <TYPE as TRAIT> as if it’s a type, and I also don’t understand why that note doesn’t talk about the lifetime 'b if we give it a name as in

impl<'a, T, I> std::fmt::Debug for MyIterable<'a, T, I> {
	fn fmt<'b>(
		&'b self,
		f: &mut std::fmt::Formatter<'_>,
	) -> std::fmt::Result {
		let x = self.as_my_iterator();
		Ok(())
    }
}

(playground)

About your note on &'a self, what about something like this:

pub trait AsRefLife<'a, T> {
    fn as_ref_life(&'a self) -> &'a [T];
}

How would I implement for Vec without &'a self?

impl<'a, T> AsRefLife<'a, T> for Vec<T> {
	fn as_ref_life(&'a self) -> &'a [T] {
		self.as_slice()
	}
}

I tend to use &'a self otherwise I never know what to do

Yes, such a trait design can make sense, and the Vec<T> example is a good one. However, taking this further.. if you implemented this trait e.g. for &[T], you would not want to make the lifetimes the same, only implementing AsRefLife<'a, T> for &'a [T], but instead you should implement AsRefLife<'a, T> for &'b [T] where 'a and 'b can be different.

(Perhaps not the best example, since such a AsRefLife trait could also be implemented by [T] directly.)

So one way to resolve the problem – if this is possible in the real context – would be by more generally implementing

impl<'a, 'b, T, I> AsMyIterator<'a, T> for MyIterable<'b, T, I> {
	fn as_my_iterator(
		&'a self
	) -> Result<
		MyIterator<'a, T>,
		(),
	> {
		todo!();
	}
}

ok, now I understood the source of the error, but why is dyn M<'a, T, I> + … invariant over 'a?

As I understood, it's mostly for mutable references.

If Foo<'a> is a trait with a lifetime argument, just because a type T implements T: Foo<'a> this wouldn't automatically mean that it implements T: Foo<'b> as-well (for shorter lifetimes 'b, i.e. 'a: 'b). You can turn such a Box<T> with T: Foo<'a> into Box<dyn Foo<'a>> though, thus you can not further convert that Box<dyn Foo<'a>> into Box<dyn Foo<'b>>; the trait object type cannot be covariant.

I see. Do you have a suggestion? The reason I'm doing this stuff is because I want to be agnostic on the memory allocator (thus I can use lots of memory allocators: either TypedArena or a simple not recyclable Vec, to allocate slices), and I also want it to work for 'static or short lifetimes, that's why the allocator can allocate Mem<'r, T, I> + Send + 's, 's is for the lifetime of self and 'r is the lifetime of the allocated data. T is the type of the slice and I is the type that indexes the slice

pub trait MemoryPool<'s, 'r, T, I> {
	fn allocate_default(
		&'s self,
		size: usize,
	) -> Box<dyn Mem<'r, T, I> + Send + 's>;
}

pub trait Mem<'r, T, I>
{
}

pub struct MyIterable<'a, T, I> {
    //this is allocated by the `MemoryPool`
	coefficients:   Box<dyn Mem<'a, T, I> + 'a>,
}

However, when I want to iterate over some object, I need to subtype to a smaller lifetime (now I understand how covariance works) by calling

pub trait AsMyIterator<'a, T> {
	fn as_my_iterator(&'a self) -> Result<MyIterator<'a, T>, ()>;
}

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

Summary of what I want to do. A trait for a MemoryPool that can allocate short-lived slices (actually, anything that can be AsRef'ed), but is agnostic on the memory allocator being used underneath, and can be parametrized on the lifetime of the allocated data (so I can use the 'static version and send to other threads)

This example still has the "trait method takes &'a self to produce Struct<'a>" issue. Inside of fmt, you only have a &'incrediblyshort self, i.e. &'incrediblyshort MyPolynomial<'a, T>. But the signature of as_poly_iterator says "you need to be able to produce a &'x self in order to get a PolyIterator<'x, T> back out." And given your implementation of the trait, that would translate to a &'x MyPolynomial<'x, T>. Note how the lifetimes match.

You naturally can't grow your 'incrediblyshort borrow to match 'a, and because your types are invariant, you can't implicitly shrink 'a to be 'incrediblyshort either. So there's no way to get a &'x MyPolynomial<'x, T> within fmt. So there's no way to utilize your implementation to get a PolyIterator<'x, T>.


If it's possible to produce a PolyIterator<'long, T> from a &'short MyPolynomial<'long, T>, then you could just not specify 'a on the &self parameter:

 pub trait AsPolyIterator<'a, T> {
-	fn as_poly_iterator(&'a self) -> Result<PolyIterator<'a, T>, ()>;
+	fn as_poly_iterator(&self) -> Result<PolyIterator<'a, T>, ()>;
 }

 impl<'a, T> AsPolyIterator<'a, T> for MyPolynomial<'a, T> {
 	fn as_poly_iterator(
-		&'a self
+		&self
 	) -> Result<

You'll get a PolyIterator<'a, T> in fmt, which may or may not be longer than desired.


If it's possible to produce a PolyIterator<'short, T> from a &'short MyPolynomial<'long, T> -- that is, MyPolynomial is probably covariant in actual behavior -- you can implement the trait as-is for all lifetimes longer than 'a:

-impl<'a, T> AsPolyIterator<'a, T> for MyPolynomial<'a, T> {
+impl<'p: 'a, 'a, T> AsPolyIterator<'a, T> for MyPolynomial<'p, T> {
 	fn as_poly_iterator(
 		&'a self
 	) -> Result<
		PolyIterator<'a, T>,
		(),
	> {

You'll get a PolyIterator<'incrediblyshort, T> in fmt. (Or even a PolyIterator<'evenmoreincrediblyshort, T>.)

This is probably the behavior you're used to (covariant in nature).


You can alternatively make a method on your structs and/or traits to explicitly perform convariance-like lifetime shortening. However, this can be tricky to do at multiple layers, like here where you have the invariant type as a field.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.