snippetrustMajor
How do I specify lifetime parameters in an associated type?
Viewed 0 times
howlifetimeassociatedspecifytypeparameters
Problem
I have this trait and simple structure:
I would like to implement the
However I'm getting this error:
I found no way to specify lifetimes inside that associated type. In particular I want to express that the iterator cannot outlive the
How do I have to modify the
Rust playground
use std::path::{Path, PathBuf};
trait Foo {
type Item: AsRef;
type Iter: Iterator;
fn get(&self) -> Self::Iter;
}
struct Bar {
v: Vec,
}I would like to implement the
Foo trait for Bar:impl Foo for Bar {
type Item = PathBuf;
type Iter = std::slice::Iter;
fn get(&self) -> Self::Iter {
self.v.iter()
}
}However I'm getting this error:
error[E0106]: missing lifetime specifier
--> src/main.rs:16:17
|
16 | type Iter = std::slice::Iter;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected lifetime parameter
I found no way to specify lifetimes inside that associated type. In particular I want to express that the iterator cannot outlive the
self lifetime.How do I have to modify the
Foo trait, or the Bar trait implementation, to make this work?Rust playground
Solution
There are a two solutions to your problem. Let's start with the simplest one:
Add a lifetime to your trait
This requires you to annotate the lifetime everywhere you use the trait. When you implement the trait, you need to do a generic implementation:
When you require the trait for a generic argument, you also need to make sure that any references to your trait object have the same lifetime:
Implement the trait for a reference to your type
Instead of implementing the trait for your type, implement it for a reference to your type. The trait never needs to know anything about lifetimes this way.
The trait function then must take its argument by value. In your case you will implement the trait for a reference:
Your
The problem with this is that the
If you want your
The
Technically, the compiler could automatically infer the lifetime in
but it's not smart enough yet.
For more complicated cases, you can use a Rust feature which is not yet implemented: Generic Associated Types (GATs). Work for that is being tracked in issue 44265.
Add a lifetime to your trait
trait Foo {
type Item: AsRef;
type Iter: Iterator;
fn get(&'a self) -> Self::Iter;
}This requires you to annotate the lifetime everywhere you use the trait. When you implement the trait, you need to do a generic implementation:
impl Foo for Bar {
type Item = &'a PathBuf;
type Iter = std::slice::Iter;
fn get(&'a self) -> Self::Iter {
self.v.iter()
}
}When you require the trait for a generic argument, you also need to make sure that any references to your trait object have the same lifetime:
fn fooget>(foo: &'a T) {}Implement the trait for a reference to your type
Instead of implementing the trait for your type, implement it for a reference to your type. The trait never needs to know anything about lifetimes this way.
The trait function then must take its argument by value. In your case you will implement the trait for a reference:
trait Foo {
type Item: AsRef;
type Iter: Iterator;
fn get(self) -> Self::Iter;
}
impl Foo for &'a Bar {
type Item = &'a PathBuf;
type Iter = std::slice::Iter;
fn get(self) -> Self::Iter {
self.v.iter()
}
}Your
fooget function now simply becomesfn fooget(foo: T) {}The problem with this is that the
fooget function doesn't know T is in reality a &Bar. When you call the get function, you are actually moving out of the foo variable. You don't move out of the object, you just move the reference. If your fooget function tries to call get twice, the function won't compile.If you want your
fooget function to only accept arguments where the Foo trait is implemented for references, you need to explicitly state this bound:fn fooget_twice(foo: &'a T)
where
&'a T: Foo,
{}The
where clause makes sure that you only call this function for references where Foo was implemented for the reference instead of the type. It may also be implemented for both.Technically, the compiler could automatically infer the lifetime in
fooget_twice so you could write it asfn fooget_twice(foo: &T)
where
&T: Foo,
{}but it's not smart enough yet.
For more complicated cases, you can use a Rust feature which is not yet implemented: Generic Associated Types (GATs). Work for that is being tracked in issue 44265.
Code Snippets
trait Foo<'a> {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&'a self) -> Self::Iter;
}impl<'a> Foo<'a> for Bar {
type Item = &'a PathBuf;
type Iter = std::slice::Iter<'a, PathBuf>;
fn get(&'a self) -> Self::Iter {
self.v.iter()
}
}fn fooget<'a, T: Foo<'a>>(foo: &'a T) {}trait Foo {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(self) -> Self::Iter;
}
impl<'a> Foo for &'a Bar {
type Item = &'a PathBuf;
type Iter = std::slice::Iter<'a, PathBuf>;
fn get(self) -> Self::Iter {
self.v.iter()
}
}fn fooget<T: Foo>(foo: T) {}Context
Stack Overflow Q#33734640, score: 69
Revisions (0)
No revisions yet.