Where would a `join` that writes to some `impl Write` go?


#1

This would be for situations where you are going to write the joined string out, or are appending to an existing String. The alternatives right now are either extra allocations and copies via join, or writing the annoying do-work-between-items loop yourself each time. Rust could get an ergonomics and performance win by having something like

impl [str] {
    fn join_onto<W: Write>(&self, sep: &str, out: &mut W) { ... }
}

I’m not sure what this method should be called, or if it makes sense more as an extension to the slice or to the writer. Thoughts?


#2

cc @bluss since I’d also like a version in intertools and you might have ideas on naming :slight_smile:


#3

It looks like you’re looking for Itertools::intersperse + String::extend

extern crate itertools;
use itertools::Itertools;

fn main() {
    let mut s = String::from("prefix: ");
    s.extend(
        ["first", "second"].iter().cloned().intersperse(", ")
    );
    assert_eq!(s, "prefix: first, second");
}

#4

Ah, intersperse will do the trick for both use cases. Thanks!


#5

I spoke too soon. The need for intersperse to be an iterator makes a bit of a mess if types or lifetimes aren’t compatible for the iterator and the interspersed element. A variant of join wouldn’t have this issue, as it’s splatting things into a Write instance.


#6

Can you post an example?


#7

Your proposal makes me think of an extension trait for Write instead that adds a method like this, which is very general. It takes a separator and an iterable of strings or &[u8]

fn write_multiple<Sep: ?Sized, T, I>(&mut self, sep: &Sep, input: I) -> Result<..>
    where Sep: AsRef<[u8]>, I: IntoIterator<Item=T>, T: AsRef<[u8]>

Itertools has two methods that solve a related problem, they use the regular formatting .format(separator, formatter_closure) and .format_default(separator). Using formatting means they can work with both std::fmt::Write and std::io::Write.

Example use:

let mut s = String::new();
let data = [1, 2, 3];
writeln!(&mut s, "{}", data.iter().format_default(" - ")).unwrap();

// s is "1 - 2 - 3"

#8

My specific example was wanting to join InternedString from libsyntax. They implement Deref<Target=str> and I think they could also implement AsRef<str> but do not. To intersperse, I’d have to also intern the separator which doesn’t seem ideal.[quote=“bluss, post:7, topic:3521”] Itertools has two methods that solve a related problem… [/quote]

Oh that’s pretty great, didn’t know about those.