Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does a lifetime mean when returning a conservative impl trait?

When searching for documentation about conservative impl trait, I found this example:

struct A {
    x: [(u32, u32); 10]
}

impl A {
    fn iter_values<'a>(&'a self) -> impl 'a + Iterator<Item = u32> {
        self.x.iter().map(|a| a.0)
    }
}

What does the lifetime 'a mean in the return type?

I am aware of this question about lifetime bound in Box, but I think that the usecases are different. If I understand well the answer:

trait object is only valid for the lifetime 'a

It means that the trait object that lives somewhere in the heap will last during a lifetime 'a.

But here, this is not a trait object but a concrete object that lives in the stack. So the compiler does not need to have hints about its lifetime.

What am I missing about this?

like image 595
Boiethios Avatar asked May 15 '18 13:05

Boiethios


2 Answers

The syntax impl Iterator<Item = u32> + 'a means

  • some type defined by the function will be returned, but you do not know the exact type. That's the impl ... part.
  • the unspecified concrete type will be an iterator of u32 values. That's the Iterator<Item = u32> part.
  • the unspecified concrete type may contain references with the lifetime 'a. That's the + 'a part.

In your example, the returned iterator contains references to self, so it must not be allowed to live longer than the instance of A, otherwise it would be invalid. The concrete type (if we could write it) would be iter::Map<slice::Iter<'a, (u32, u32)>, <closure>> — note that it has a 'a in it.

It means that the trait object that lives somewhere in the heap will last during a lifetime 'a.

This is not quite true. Both cases have the same meaning: the unspecified concrete type may contain a reference. With a trait object, the concrete type is behind a pointer of some kind (Box, &, Rc, etc.). With impl trait, the concrete type is placed directly on the stack.

this is not a trait object but a concrete object that lives in the stack

Trait objects do not require the heap; they can utilize only the stack:

let x: &std::fmt::Display = &42;
println!("{}", x);

See also:

  • Why are explicit lifetimes needed in Rust?
  • Are polymorphic variables allowed?
like image 67
Shepmaster Avatar answered Oct 20 '22 22:10

Shepmaster


It means that the trait object that lives somewhere in the heap will last during a lifetime 'a.

Not quite.

'a here does not exactly specify, it only places an upper-bound on the lifetime of the object. Whether the object is located on the heap or on the stack does not matter: the compiler must ensure that the lifetime of this object does not exceed 'a.

The lifetime represents a relationship between referred and referent, and is used to ensure that the referent will never outlive the referred. As such, it places an upper-bound on the lifetime of the referent and a lower-bound on the lifetime of the referred.


The compiler could derive the necessary lifetime from the actual concrete type that is returned by the function, however it would require that the type-checker be able to look at the function implementation to perform its job.

Documenting the lifetime constraint at the interface boundary is thus more friendly to both humans and compiler: it allows local reasoning.

like image 23
Matthieu M. Avatar answered Oct 21 '22 00:10

Matthieu M.