Trait bound in function's signature, what is the error?

The code is basically taken from the post, I made some motification, but it failed to pass the compiling. layground

I don't know why.

trait Iterable<'a: 'b, 'b, T: ?Sized>: 'a {
    fn boxed_iter(&'b self) -> Box<dyn Iterator<Item = &'b T> + 'b>;
}

impl<'a: 'b, 'b, T: ?Sized + 'a> IntoIterator for &'b (dyn Iterable<'a, 'b, T> + 'a) {
    type Item = &'b T;
    type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'b>;
    fn into_iter(self) -> Self::IntoIter {
        self.boxed_iter()
    }
}

fn main() {}

I guess it maybe the Self in boxed_iter(&'b self) is &'b (dyn Iterable<'a, 'b, T> + 'a), but &'b (dyn Iterable<'a, 'b, T> + 'a) is not a Iterable.

And method calls will try receiver as the Self at first, then the calling will have the form of Iterable::boxed_iter(&'b (dyn Iterable<'a, 'b, T> + 'a)), will fit the signature of boxed_iter well.

But if this is what is happening, why the code is refued?

Given 'a: 'b, 'b, T: ?Sized + 'a, can dyn Iterable<'a, 'b, T> + 'a implement Iterable<'a, 'b, T> ?

trait Iterable<'a: 'b, 'b, T: ?Sized>: 'a
  • 'a: 'b? :white_check_mark:
  • dyn Iterable<'a, 'b, T> + 'a: 'a?
    • 'a: 'a? :white_check_mark:
    • T: 'a? :white_check_mark:
    • 'b: 'a? :x:

Remove the 'a bound from the trait and it compiles. (The entire lifetime 'a isn't doing anything useful in your playground, so better yet, remove it entirely.)

Why are you trying to introduce two lifetimes for one (covariant) thing? You are, once again, catastrophically overcomplicating it. You don't even need a single explicit lifetime for the trait definition, everything can be elided.

This compiles.

1 Like

I am trying fix the weakness of @quinedot mentioned in that post

fn run_owned(foo: &dyn Foo, iter: Box<dyn Iterable<'_, str> + '_>) {
    foo.process_strings(&*iter);
}

Not succeed until now :sweat_smile:

I don't see the problem, Quinedot's solution is basically the same as the one I just posted. Due to covariance, it compiles and runs even with the owned-boxed argument.

I haven't read his solution yet, trying to find a method by myself

Point is, if you remove the useless lifetime annotations, and let the compiler infer most of it, then all of what you want compiles and runs correctly.

I felt I already wrote too much over there, so I didn't follow up, but FYI in case you keep playing with it -- ah I see perhaps you don't want spoilers too.

Click to reveal.

IIRC a key part of avoiding HRTB sadness is to avoid introducing a type variable...

// OP
//                       v
impl<'a, I: 'a + ?Sized, T: Borrow<O> + 'a + ?Sized, O: ?Sized> Iterable<'a, O> for I
where
    &'a I: IntoIterator<Item = &'a T>,
// My long explanation
//                               vvvv
impl<Iter: ?Sized, Item: ?Sized, Temp: ?Sized> Iterable<Item> for Iter
where
    for<'any> &'any Iter: IntoIterator<Item = &'any Temp>>,
    Temp: Borrow<Item>,

You don't actually need the new syntax

for<'any> &'any Iter: IntoIterator<Item: RefInto<&'any Item>>

if you instead use

for<'any> &'any Iter: IntoIterator,
for<'any> <&'any Iter as IntoIterator>>::Item: RefInto<&'any Item>

but avoiding the type variable is important, because we have no good way to write something like

for<'any> &'any Iter: IntoIterator<Item = &impl Borrow<Item>>

or anything else that introduces the type underneath the binder so it could act like

for<'any where Temp: 'any> ...

[1]

(I didn't go back and test everything I just wrote.)


  1. we have some bad ways that fall apart if strained too hard ↩︎

Finally I make the code compiled.

There are still some unclear part for me: why explicitly writing some bounds in the most basic definition will cause this bound be written everywhere?

For example: this code is OK:

trait Iterable<'b, U: ?Sized> {
    fn boxed_iter<'d>(&'d self) -> Box<dyn Iterator<Item = &'d U> + 'd>
    where
        'b: 'd;
}

impl<'b, I: ?Sized, T: Borrow<O> + 'b + ?Sized, O: ?Sized> Iterable<'b, O> for I
where
    for<'c> &'c I: IntoIterator<Item = &'c T>,
{
    fn boxed_iter<'d>(&'d self) -> Box<dyn Iterator<Item = &'d O> + 'd>
    where
        'b: 'd,
    {
        Box::new(self.into_iter().map(Borrow::borrow))
    }
}

If I modify the code by adding explicit bound to Iterable, then I have to add it to the other place.

However, I added it to the most basic definition so that rustc knows about this bound, thereby letting it know about this constraint, to avoid having to write it again in other places.

trait Iterable<'b, U: 'b + ?Sized> {
                    // ^ ----------------- add this
    fn boxed_iter<'d>(&'d self) -> Box<dyn Iterator<Item = &'d U> + 'd>
    where
        'b: 'd;
}

impl<'b, I: ?Sized, T: Borrow<O> + 'b + ?Sized, O: 'b + ?Sized> Iterable<'b, O> for I
                                                 // ^ ------ then I have to add this
where
    for<'c> &'c I: IntoIterator<Item = &'c T>,
{
    fn boxed_iter<'d>(&'d self) -> Box<dyn Iterator<Item = &'d O> + 'd>
    where
        'b: 'd,
    {
        Box::new(self.into_iter().map(Borrow::borrow))
    }
}

1 Like

And I wandering is there something like

for<'c> &'c I: IntoIterator<Item = &'c T> where 'c: 'd

and rustc can conclude T: 'd as a result

(I'm working off your shorter example and not the entire working playground for this portion.)

Only bounds Self are implied elsewhere.[1] The bound you added was a new restriction by the way, in case you thought it was already true.

There's some tricks like mentioning &'d &'c () that makes 'c: 'd implicit, but it can be tricky or limiting to work that in underneath the binder. We don't have explicit conditional binders so far.

for<'c where 'c: 'd> ...

  1. "supertrait bounds", but lifetimes too ↩︎

OK, I looked at your playground. Here it is with no significant changes (just tidied up the names of generics and unelided some lifetimes).

It's nicer than mine in the sense that you didn't need a helper trait and it still solved the "borrow forever", run_owned issue. It's not fully HRTB-friendly (uncomment check_hr to see the error), but it's flexible enough that it probably doesn't need to be. The lifetime parameter is invariant which could cause some problems, but that's probably extremely niche too.

You should should post it as an alternative in the other thread.

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.