#iterator #write #fmt #separator #items #allocations #interspersed

no-std fmt-interspersed

A crate to write an iterator's items, interspersed with a separator, to a destination

13 releases (4 breaking)

0.5.0 Nov 4, 2024
0.4.0 Nov 3, 2024
0.3.2 Oct 23, 2024
0.2.9 Oct 14, 2024
0.1.2 Oct 13, 2024

#470 in Rust patterns

Download history 690/week @ 2024-10-11 326/week @ 2024-10-18 36/week @ 2024-10-25 263/week @ 2024-11-01 38/week @ 2024-11-08 3/week @ 2024-11-15

306 downloads per month

MIT license

24KB
411 lines

fmt-interspersed

github build license crates.io docs.rs msrv

This crate provides analogs of the std::fmt macros such as format! and write! to make it easier to “stringify” the contents of an iterator interspersed with a separator without any intermediate allocations. The items yielded by the iterator do not need to be the same type as the separator.

use fmt_interspersed::prelude::*;

let s = "abc";
assert_eq!("a0b0c", format_interspersed!(s.chars(), 0));

Without this crate, the above would look something like the following. (Indeed, the implementation of format_interspersed! is nearly identical.)

use std::fmt::Write;

let mut buf = String::new();
let s = "abc";
let sep = 0;

let mut iter = s.chars();
if let Some(c) = iter.next() {
    write!(buf, "{c}").unwrap();
    for c in iter {
        write!(buf, "{sep}").unwrap();
        write!(buf, "{c}").unwrap();
    }
}

assert_eq!("a0b0c", buf);

In the above, s.chars()::Item implements std::fmt::Display. But you can specify a custom format to use to display the items, which is useful when the iterator’s items aren't Display or need customization. This takes the form of pattern => fmt_args... as the final argument. (The separator is always stringified using its Display implementation and must implement Display.)

let pairs = vec![("a", 1), ("b", 2)];
assert_eq!(
    r#"(x: "a", y: 1); (x: "b", y: 2)"#,
    format_interspersed!(pairs, "; ", (x, y) => "(x: {x:?}, y: {y})")
);

There are equivalents of all of the format_args!-related macros (except for format_args! itself), so you can, for example, write to a string, file, or buffer without allocating any intermediate strings:

// as with `write!`, the necessary trait for writing, either `fmt::Write`
// (for strings) or `io::Write` (for files or other byte sinks), must be in scope
use std::fmt::Write;

let mut buf = String::new();
write_interspersed!(buf, 1_i32..=5, '-', n => "{:02}", n.pow(2))?;
assert_eq!("01-04-09-16-25", buf);
use std::io::{Cursor, Write};

let mut buf = Cursor::new(Vec::<u8>::new());
writeln_interspersed!(buf, "abc".bytes(), ',', b => "{}", b - b'a')?;
write_interspersed!(buf, "abc".bytes(), ',', b => "{}", (b - b'a' + b'A') as char)?;
assert_eq!("0,1,2\nA,B,C", String::from_utf8(buf.into_inner()).unwrap());

Macros, features, and no_std

This crate has two features: alloc and std. std is enabled by default and implies alloc. With alloc and std disabled, this crate is #![no_std]-compatible.

Below is the list of this crate’s macros and the features that enable them:

No runtime deps

~0–320KB