itertools icon indicating copy to clipboard operation
itertools copied to clipboard

Format should not panic if you try to format it more than once

Open zxqfl opened this issue 7 years ago • 2 comments

Some logging frameworks will format things sent to the error! macro twice.

Some possible solutions:

  1. Print a "iterator exhausted" message instead of panicking
  2. Impose a Clone bound on the iterator being formatted so that we can format it multiple times
  3. Doesn't fmt return a Result anyway? Why not just return Err?

zxqfl avatar Sep 14 '18 21:09 zxqfl

(3) From previous discussion I have understood that fmt's error return should not be used by the thing that is being formatted. It's used to relay the information that an I/O error happened through the formatting chain.

I don't know how to solve it, I think this is a drawback that comes from our skirting the rules a bit for formatting.

bluss avatar Nov 28 '18 16:11 bluss

As an alternative to changing the behavior of the existing format, how about a separate extension-trait on copyable sources of iterators providing a "safe format" type, so that into_iter() is called every time fmt is called?

trait IterSafeFormatExt {
    type I;
    fn safe_format(&self, separator: &'static str) -> SafeFormat<Self::I>;
}
struct SafeFormat<I>{
    iterable: I,
    separator: &'static str,
}
impl<I: IntoIterator<Item = E> + Copy, E: fmt::Display> fmt::Display for SafeFormat<I> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.iterable.into_iter().format(self.separator).fmt(f)
    }
}
impl<I: IntoIterator<Item = E> + Copy, E: fmt::Display> IterSafeFormatExt for I {
    type I = I;
    fn safe_format(&self, separator: &'static str) -> SafeFormat<I> {
        SafeFormat { iterable: *self, separator }
    }
}

The common case would be to call safe_format on a shared reference to a container:

fn main() {
    let mut v = Vec::new();
    v.push("foo");
    let formatter = (&v).safe_format(", ");
    println!(
        "{}{}",
        formatter, formatter,
    );
}

Edit: I'd like to have some way to accept an arbitrary callback that generates the appropriate iterator on-demand, but I can't figure out how to make the types work out for that.

BatmanAoD avatar Apr 10 '25 22:04 BatmanAoD