How to name an associated type on a type with a lifetime bound? - rust

I am trying to implement IntoIterator for a type which needs to yield an immutable reference of it's inner type like so:
impl<T> IntoIterator for Ref<T>
where
for<'a> &'a T: IntoIterator,
{
type Item = T::Item;
type IntoIter = T::IntoIter;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
However, the above does not type-check because I declared that &'a T: IntoIterator not T: IntoIterator, and therefore T::Item won't work...how can I name this associated type?

Right after I typed this question, a solution that works came to me, I can just use generics to name the inner types like so:
impl<T, Item, IntoIter> IntoIterator for Ref<T>
where
for<'a> &'a T: IntoIterator<Item = Item, IntoIter = IntoIter>,
IntoIter: Iterator<Item = Item>,
{
type Item = Item;
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
Hopefully this helps anyone else who may run into this seemingly nefarious situation with associated types. :)

Related

Lifetime and associated types

I have this enum:
enum Node<T> {
Leaf(T),
Children(Vec<Node<T>>),
}
And want to implement the Iterator trait for Node.
I created this struct and tried to implement the IntoIterator trait:
struct NodeIter<'a, T>{
children: &'a [Node<T>],
parent: Option<&'a Node<T>>,
}
impl<'a, T> IntoIterator for Node<T> {
type Item = T;
type IntoIter = NodeIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
I can not figure out the correct lifetime specifiers, I end up getting this error:
error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
--> src/tree_my.rs:44:6
|
44 | impl<'a, T> IntoIterator for Node<T> {
| ^^ unconstrained lifetime parameter
I am new to Rust and I am not sure if I am doing it wrong or if it is not possible. Because I have read about similar problems. The problem seems to have something to do with traits (IntoIterator in my case) and associated types. I also read something about GAT.
Maybe someone could explains this problem and how one would solve it.
Rust Playground
IntoIterator::into_iter consumes its argument. This means that once you've called node.into_iter() the node no longer exists, but it looks like you want your NodeIter to keep references to node which is impossible since it's no longer around.
You will need to either change NodeIter to take ownership of the original Node so that NodeIter can keep the Node alive for as long as it needs it, or implement IntoIter for references to Node (which will consume the reference, but keep the original Node intact):
enum Node<T> {
Leaf(T),
Children(Vec<Node<T>>),
}
struct NodeIter<'a, T>{
children: &'a [Node<T>],
parent: Option<&'a Node<T>>,
}
impl<'a, T> Iterator for NodeIter<'a, T> {
type Item = T;
fn next (&mut self) -> Option<Self::Item> {
todo!();
}
}
impl<'a, T> IntoIterator for &'a Node<T> {
type Item = T;
type IntoIter = NodeIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
Playground

Rust: implement trait for the associated type

In the snippet below (https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5babd9991122c756e7afaa76da0b32f3)
#![feature(generic_associated_types)]
#![feature(marker_trait_attr)]
// StrSpan is opaque and imported from nom_locate
struct StrSpan<'a>(&'a str);
pub trait LifetimizedExt {
type Lifetimized<'a>;
}
impl<T> LifetimizedExt for Option<T>
where
T: LifetimizedExt
{
type Lifetimized<'a> = Option<T::Lifetimized<'a>>;
}
impl<'a,T> LifetimizedExt for &'a T
where
T: 'static + ?Sized
{
type Lifetimized<'b> = &'b T;
}
impl<'a,T> LifetimizedExt for &'a mut T
where
T: 'static + ?Sized
{
type Lifetimized<'b> = &'b mut T;
}
#[marker]
pub trait ParsableInput: LifetimizedExt {}
impl<'a> ParsableInput for &'a str {}
impl<'a> ParsableInput for StrSpan<'a> {}
impl<'a,I> ParsableInput for I::Lifetimized<'a>
where
I: ParsableInput
{}
specifically in
impl<'a,I> ParsableInput for I::Lifetimized<'a>
where
I: ParsableInput
{}
I is unconstrained.
According to https://doc.rust-lang.org/error-index.html#E0207,
Any type parameter of an impl must meet at least one of the following criteria:
it appears in the implementing type of the impl, e.g. impl Foo
for a trait impl,
it appears in the implemented trait, e.g. impl SomeTrait > for Foo
it is bound as an associated type, e.g. impl<T, U> SomeTrait for T where T: AnotherTrait<AssocType=U>
How do I rewrite the original snippet?
I always want to use ParsableInput trait to infer that I::Lifetimized<'a> is ParsableInput for any lifetime 'a. Is it possible?
Requested addition.
The reasons why such behavior is desired are that
It's just stating the important fact about the concerned types
Nom combinators require specific type signatures for functions to be used as their arguments.
They are generic as well. For example, line_ending. It requires T: InputIter + InputLength.
Without generic associated types, there will be too much repetition, and even one small combinatorial explosion (NxM implementations).
pub trait Parse<'a,I>: Sized
where
I: ParsableInput,
Self: LifetimizedExt<Lifetimized<'a> = Self>
{
/// Returns [`Result<P,E>`] where any [`Ok(p)`] is a pair `(i,val)`, s.t.
/// * `i` is the remaining input after parsing
/// * `val` is the parsed value
fn parse<'b, 'c>(i: I::Lifetimized<'b>) -> IResult<I::Lifetimized<'c>, Self::Lifetimized<'a>>
where
'b: 'c,
'b: 'a;
}
Addition #2: "Why LifetimizedExt exists"
Its associated generic type Lifetimized is the reification of the class of types equivalent to Self up to lifetime. I needed to make implementations of Parse::parse for (&'b str,&'c str) and for (StrSpan<'b>,StrSpan<'c>) (modulo other details). Theoretically, I could supply a pair-type-argument but (sic) parse wouldn't be generic.
Note: Now I realize that it could be generic if the constructed triple (&'a str,&'b str,&'c str) satisfied some trait but it would make things even more complicated.
Addition #3: "Why was genericity in Parse::parse needed"
There also was map_parsed_val function,
// Proper declaration and implementation requires GATs
use nom::IResult;
pub trait MapParsedValInTuple<'a, T>
where
for<'c> T: super::Parse<'a,&'c str>,
{
fn map_parsed_val<'b, U, F: FnOnce(T) -> U>(self, f: F) -> (&'b str, U)
where
'a: 'b;
}
impl<'a, T> MapParsedValInTuple<'a, T> for (&'a str, T)
where
for<'c> T: super::Parse<'a,&'c str>,
{
fn map_parsed_val<'b, U, F: FnOnce(T) -> U>(self, f: F) -> (&'b str, U)
where
'a: 'b,
{
let (remaining_input,parsed_val) = (self.0, self.1);
(remaining_input, f(parsed_val))
}
}
pub trait MapParsedValInResult<'a, T> {
fn map_parsed_val<'b, U, F: FnOnce(T) -> U>(self, f: F) -> IResult<&'b str, U>
where
'a: 'b;
}
impl<'a, T> MapParsedValInResult<'a, T> for IResult<&'a str, T> {
fn map_parsed_val<'b, U, F: FnOnce(T) -> U>(self, f: F) -> IResult<&'b str, U>
where
'a: 'b,
{
self.map(|(i, parsed_val)| (i, f(parsed_val)))
}
}
I haven't rewritten it yet but you can see that GATs would be nifty here. Also, you can notice that it deals only with types whose type-parameter for Parse is &str. With the addition of StrSpan, in this place too would happen combinatorial explosion. It's also important to note that it ends life of the parsed value while keeping the lifetime of the remaining input intact. How long they will live, how the function will be used, is unknown (and irrelevant as long as genericity in the method is there). It also makes sense that this method should be generic.

How to create an `Iterable` trait for references in Rust?

I'm trying to create a trait that captures the iter function in slice as well as VecDeque, BTreeMap and HashMap. I'd like the implementer of this trait to be able to specify and implement their own iterator type, but it looks like this iterator type must have a lifetime argument, and that cannot be given as an associated type.
In more detail, here's what I wish was possible in Rust:
trait RefIterable<T>
where for<'a> (T: 'a) => (Self::Iter<'a>: Iterator<Item = &'a T>)
{
type Iter; // Has kind (lifetime -> type)
fn refs<'a>(&'a self) -> Self::Iter<'a>
}
If this was possible, the implementation could look like this
impl RefIterable<T> for Vec<T> {
type Iter<'a> = std::slice::Iter<'a, T>; // This is not valid Rust code.
fn refs<'a>(&'a self) -> std::slice::Iter<'a, T> {
self.as_slice().iter()
}
}
I'm still relatively new to Rust, so I'm asking if there's already a way to do this that I'm not aware of, or if there's a nice workaround for this situation. I'd imagine that this situation is not very rare.
(Using Box<dyn 'a + Iterator<Item = &'a T>> is my current workaround, but that prevents some optimization from happening.)
Edit:
EvilTak's answer is probably the best thing we can do right now. The ability to combine all possible lifetimes together with the condition T: 'a into one unparametrized trait seems to be unsupported by Rust as of today.
Add the lifetime parameter to the trait instead, which allows you to use it in the associated type Iter's bound:
trait RefIterable<'a> {
type Item: 'a;
type Iter: Iterator<Item = &'a Self::Item>; // Has kind (lifetime -> type)
fn refs(&'a self) -> Self::Iter;
}
The Item: 'a bound is required to let the compiler know that the references (&'a Self::Item) do not outlive the type (Self::Item).
I have modified RefIterable to make it follow Iterator's convention of using an associated type to specify the type of the items that are iterated over for the same reason as the one behind Iterator's usage of an associated type.
Implementations are pretty straightforward:
impl<'a, T: 'a> RefIterable<'a> for Vec<T> {
type Item = T;
type Iter = std::slice::Iter<'a, T>;
fn refs(&'a self) -> std::slice::Iter<'a, T> {
self.as_slice().iter()
}
}
Playground

Issues constraining implementation lifetimes on type without lifetime parameter

I'm trying to implement a BST in Rust (for HW3 in this lovely intro to Rust), and I'm running into errors with lifetimes, and how to constrain lifetimes for types that are related to types without a lifetime.
#[derive(Debug)]
pub struct BST<T>
where T: Ord
{
root: Option<Box<Node<T>>>,
}
// A couple dozen lines of BST stuff
impl<'a, T> IntoIterator for BST<T>
where T: Ord
{
type Item = T;
type IntoIter = BSTIter<'a, T>; // <- my intuition is that I should
// be able to say "BSTIter lives as
// long as BST."
fn into_iter(&'a mut self) -> BSTIter<'a, T> {
BSTIter::new(&mut self)
}
}
pub struct BSTIter<'a, T: 'a>
where T: Ord + 'a
{
bst: &'a mut BST<T>,
node_list: Vec<&'a Node<T>>, // this is where the need for a lifetime on
// BSTIter comes from
}
impl<'a, T> BSTIter<'a, T>
where T: Ord
{
fn new(&mut bst: BST<T>) -> BSTIter<'a, T> {
let traverse_stack = Vec::new();
if let Some(ref x) = bst.root {
traverse_stack.push(x);
}
BSTIter {
bst: bst,
node_list: traverse_stack,
}
}
}
impl<'a, T> Iterator for BSTIter<'a, T>
where T: Ord
{
type Item = T;
fn next(&mut self) -> Option<T> {
// BST iteration details
}
}
As it stands, this code spits out the error
error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
--> src/lib.rs:117:7
|
117 | impl<'a, T> IntoIterator for BST <T> where T: Ord {
| ^^ unconstrained lifetime parameter
If the IntoIterator trait didn't require me to specify type IntoIterator = BSTIter, the implementation block could just have an into_iter method signature of into_iter<'a>(&'a mut self) -> BSTIter<'a, T>. Since I need to specify the lifetime for BSTIter, it seems like I need to specify a lifetime for the entire BST type. Again, my instinct says that I shouldn't have to specify a lifetime on BST to be able to create an iterator for it.
I realize the two solutions to this are likely one (or both) of
There's a language feature that helps me get around this
Somewhere along the way, my code became very much not idiomatic Rust
If I could get help on either how to make the above code snippet work, or how I should be approaching these lifetime and ownership details in general, it would be very much appreciated!
You've misunderstood the purpose and usage of IntoIterator. It converts a value into an iterator; consuming the value in the process. However, your iterator is attempting to return references to the collection. You cannot return references into the iterator, so it makes no sense to consume the tree, transferring ownership to the iterator.
The fact that you've called it BSTIter instead of BSTIntoIter shows promise; as that's the idiomatic name for an iterator that returns references.
You want to implement IntoIterator for &'a BST<T>, not BST<T>. You could also implement it for BST<T>, but then you'd want to yield T, not &T.
After fixing that, there's lots of compiler errors: mismatched types all throughout the code, incorrect method signatures in traits (fn into_iter(self) is all you are allowed), for some reason there's a mutable reference to the tree, variables aren't mutable when they should be....
#[derive(Debug)]
struct Node<T>(Option<Box<T>>);
#[derive(Debug)]
pub struct BST<T>
where T: Ord
{
root: Option<Box<Node<T>>>,
}
impl<'a, T> IntoIterator for &'a BST<T>
where T: Ord
{
type Item = T;
type IntoIter = BSTIter<'a, T>;
fn into_iter(self) -> BSTIter<'a, T> {
BSTIter::new(self)
}
}
pub struct BSTIter<'a, T: 'a>
where T: Ord + 'a
{
bst: &'a BST<T>,
node_list: Vec<&'a Node<T>>,
}
impl<'a, T> BSTIter<'a, T>
where T: Ord
{
fn new(bst: &'a BST<T>) -> BSTIter<'a, T> {
let mut traverse_stack = Vec::new();
if let Some(ref x) = bst.root {
traverse_stack.push(&**x);
}
BSTIter {
bst: bst,
node_list: traverse_stack,
}
}
}
impl<'a, T> Iterator for BSTIter<'a, T>
where T: Ord
{
type Item = T;
fn next(&mut self) -> Option<T> {
None
}
}
fn main() {}

How do I specify lifetime parameters in an associated type?

I have this trait and simple structure:
use std::path::{Path, PathBuf};
trait Foo {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&self) -> Self::Iter;
}
struct Bar {
v: Vec<PathBuf>,
}
I would like to implement the Foo trait for Bar:
impl Foo for Bar {
type Item = PathBuf;
type Iter = std::slice::Iter<PathBuf>;
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<PathBuf>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ 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
There are a two solutions to your problem. Let's start with the simplest one:
Add a lifetime to your trait
trait Foo<'a> {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
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<'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()
}
}
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<'a, T: Foo<'a>>(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<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()
}
}
Your fooget function now simply becomes
fn fooget<T: Foo>(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<'a, T>(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 as
fn fooget_twice<T>(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.
Use a wrapper type
If the trait and all its implementations are defined in one crate, a helper type can be useful:
trait Foo {
fn get<'a>(&'a self) -> IterableFoo<'a, Self> {
IterableFoo(self)
}
}
struct IterableFoo<'a, T: ?Sized + Foo>(pub &'a T);
For a concrete type that implements Foo, implement the iterator conversion on the IterableFoo wrapping it:
impl Foo for Bar {}
impl<'a> IntoIterator for IterableFoo<'a, Bar> {
type Item = &'a PathBuf;
type IntoIter = std::slice::Iter<'a, PathBuf>;
fn into_iter(self) -> Self::IntoIter {
self.0.v.iter()
}
}
This solution does not allow implementations in a different crate. Another disadvantage is that an IntoIterator bound cannot be encoded into the definition of the trait, so it will need to be specified as an additional (and higher-rank) bound for generic code that wants to iterate over the result of Foo::get:
fn use_foo_get<T>(foo: &T)
where
T: Foo,
for<'a> IterableFoo<'a, T>: IntoIterator,
for<'a> <IterableFoo<'a, T> as IntoIterator>::Item: AsRef<Path>
{
for p in foo.get() {
println!("{}", p.as_ref().to_string_lossy());
}
}
Associated type for an internal object providing desired functionality
The trait can define an associated type that gives access to a part of the object that, bound in a reference, provides the necessary access traits.
trait Foo {
type Iterable: ?Sized;
fn get(&self) -> &Self::Iterable;
}
This requires that any implementation type contains a part that can be so exposed:
impl Foo for Bar {
type Iterable = [PathBuf];
fn get(&self) -> &Self::Iterable {
&self.v
}
}
Put bounds on the reference to the associated type in generic code that uses the the result of get:
fn use_foo_get<'a, T>(foo: &'a T)
where
T: Foo,
&'a T::Iterable: IntoIterator,
<&'a T::Iterable as IntoIterator>::Item: AsRef<Path>
{
for p in foo.get() {
println!("{}", p.as_ref().to_string_lossy());
}
}
This solution permits implementations outside of the trait definition crate.
The bound work at generic use sites is as annoying as with the previous solution.
An implementing type may need an internal shell struct with the only purpose of providing the associated type, in case when the use-site bounds are not as readily satisfied as with Vec and IntoIterator in the example discussed.
In future, you'll want an associated type constructor for your lifetime 'a but Rust does not support that yet. See RFC 1598

Resources