How can I implement std::fmt::Display for a custom

2019-07-26 10:37发布

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.

标签: rust
2条回答
啃猪蹄的小仙女
2楼-- · 2019-07-26 10:46

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.

查看更多
时光不老,我们不散
3楼-- · 2019-07-26 10:56

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()
    }
}
查看更多
登录 后发表回答