4

LimitedFifoQueue is a struct that wraps the functionality of a VecDeque to limit the number of items it will store at any time:

use std::collections::{vec_deque, VecDeque};
use std::fmt;
use std;

#[derive(Debug)]
pub struct LimitedFifoQueue<T> {
    size: usize,
    store: VecDeque<T>,
}

impl<T> LimitedFifoQueue<T> where T: fmt::Display {
    pub fn new(size: usize) -> LimitedFifoQueue<T> {
        LimitedFifoQueue {
            size: size,
            store: VecDeque::with_capacity(size),
        }
    }
    pub fn push(&mut self, elem: T) {
        self.store.push_front(elem);
        if self.store.len() > self.size {
            self.store.pop_back();
        }
    }
    pub fn clear(&mut self) {
        self.store.clear();
    }
}

I've implemented the IntoIterator trait as follows:

impl<T> IntoIterator for LimitedFifoQueue<T> where T: fmt::Display {
    type Item = T;
    type IntoIter = vec_deque::IntoIter<T>;
    fn into_iter(self) -> Self::IntoIter {
        self.store.into_iter()
    }
}

And a simplified function that loops through and prints each Item:

fn print_all<I>(lines: &I) where I: IntoIterator {
    for string in lines.into_iter() {
        println!("{}", string);
    }
}

This gives me the following error:

println!("{}", string);
               ^^^^^^ the trait `std::fmt::Display` is not implemented for `<I as std::iter::IntoIterator>::Item`

I have created a playground of the code with a full stack trace here.


Also, I'm aware that there may be a better way to accomplish what I'm trying to do. I'd love to hear any additional suggestions.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Patrick Allen
  • 2,148
  • 2
  • 16
  • 20

2 Answers2

5

How can I implement std::fmt::Display for a custom IntoIterator::Item?

You cannot. Item might be a type you don't own, and Display is a trait you don't own. You cannot implement a trait you don't own for a type you don't own.

All you can do is require that Item implements Display:

fn print_all<I>(lines: I)
    where I: IntoIterator,
          I::Item: fmt::Display, 
{
    for string in lines.into_iter() {
        println!("{}", string);
    }
}

You don't need any of the other T: Display bounds on your data structure or its methods, as none of those implementations care to print out a value.

Incidentally, into_iter is automatically called on the for-loops argument, so you only need to say:

fn print_all<I>(lines: I)
    where I: IntoIterator,
          I::Item: fmt::Display, 
{
    for string in lines {
        println!("{}", string);
    }
}

You may also wish to review How to implement Iterator and IntoIterator for a simple struct?, as you are passing &lfq into print_all, but &LimitedFifoQueue doesn't implement IntoIterator, only LimitedFifoQueue does. These are different types. You'll need something like

impl<'a, T> IntoIterator for &'a LimitedFifoQueue<T> {
    type Item = &'a T;
    type IntoIter = vec_deque::Iter<'a, T>;
    fn into_iter(self) -> Self::IntoIter {
        self.store.iter()
    }
}
Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Cool, looking at the link now. To clarify: If I'm passing &lfq and need to iterate over it, what traits need to be implemented for LimitedFifoQueue? What do I have to specify in the annotation? Thanks again @Shepmaster – Patrick Allen Mar 28 '17 at 23:45
  • Okay, I think I'm getting closer... I added the suggested trait implementation and then annotated `print_all` with a lifetime bound, and `&'a I::IntoIter: Iterator` bound. It's now telling me that `&_` does not have the trait Iterator in `print_all`. What gives? [New Playground Link](https://play.rust-lang.org/?gist=e46d4c5071563f66291267af763b3378&version=stable&backtrace=0) – Patrick Allen Mar 29 '17 at 01:03
  • @PatrickAllen why did you annotate it with a lifetime bound? The same implementation shown above works. – Shepmaster Mar 29 '17 at 02:06
  • Wow. You're right. I just went down a crazy rabbit hole because I had slipped and had `fn print_all(lines: &I)`. Thanks so much :) – Patrick Allen Mar 29 '17 at 02:43
2

This problem has nothing to do with your IntoIterator implementation or the rest of your type definition. Just take a look at this code:

fn print_all<I>(lines: &I) where I: IntoIterator {
    for string in lines.into_iter() {
        println!("{}", string);
    }
}

This piece of code doesn't even know about your LimitedFifoQueue type! It takes a value of generic type I. What do we know about I? It implements IntoIterator. Great, what does that tell us about the values the iterator will spit out? Nothing!

So it could be anything, in particular also stuff that doesn't implement fmt::Display. So what we want to do is to annotate that the items of the iterator should at least implement fmt::Display. How is that done? By adding a bound to the associated type Item of the IntoIterator trait:

fn print_all<I>(lines: &I)
    where I: IntoIterator,
          I::Item: fmt::Display,
{ ... }

Once you understand that you can also add bounds to associated items this makes intuitive sense.

After you fixed that error, another error will be reported, about "moving out of borrowed content". This is a fairly standard error, which I won't explain in detail here. But in summary: your print_all() function should receive a I instead of a &I.

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
  • Thanks, very true regarding the &I. I however actually need it to be borrowed as it is owned by another struct. What will be required for me to be able to iterate over this borrowed &lfq? – Patrick Allen Mar 28 '17 at 23:48