1

I'm creating a method to format data out of an iterator. To allow chaining, I'm trying to provide it as new method of Iterator through generics :

trait ToSeparatedString {
    fn to_separated_string(self, line_prefix: &str, separator: &str) -> String;
}

impl<T, I> ToSeparatedString for I
where
    T: Display,
    I: Iterator<Item = T> + Clone,
{
    fn to_separated_string(self, line_prefix: &str, separator: &str) -> String {
        let len = self.clone().count();

        self.enumerate()
            .map(|(i, line)| if i < len - 1 {
                (line, separator)
            } else {
                (line, "")
            })
            .fold::<String, _>("".to_owned(), |acc, (line, line_end)| {
                format!("{}{}{}{}", acc, line_prefix, line, line_end)
            })
    }
}

Then I'm using it here :

#[derive(Debug)]
pub struct TransactionDocumentBuilder<'a> {
    /// Currency Id.
    pub currency: &'a str,
    /// Document timestamp.
    pub blockstamp: Blockstamp,
    /// Transaction locktime (in seconds ?)
    pub locktime: u64,
    /// List of issuers.
    pub issuers: Vec<ed25519::PublicKey>,
    /// List of inputs.
    pub inputs: Vec<Input>,
    /// List of outputs.
    pub outputs: Vec<Output>,
    /// Transaction comment.
    pub comment: &'a str,
}

impl<'a> DocumentBuilder<TransactionDocument> for TransactionDocumentBuilder<'a> {
    fn build_with_signature(self, signature: ed25519::Signature) -> TransactionDocument {
        TransactionDocument {
            document: GenericDocumentBuilder::new(10, "Transaction", self.currency)
                .with("Blockstamp", &self.blockstamp.to_string())
                .with("Locktime", &self.locktime.to_string())
                .with("Issuers", &self.issuers.iter().to_separated_string("", "\n"))
                .with("Inputs", &self.inputs.iter()
                    .map(|input| input.source)
                    .to_separated_string("", " "))
                // Iterate through each input unlocks
                .with("Unlocks", &self.inputs.iter()
                    .enumerate()
                    .map(|(i, input)| {
                        input.unlocks.iter().to_separated_string(&i.to_string(), "\n")
                    })
                    .to_separated_string("", "\n")
                )
                // more fields
                .build_with_signature(signature),
        };

        unimplemented!()
    }

    fn build_and_sign(self, _private_key: &ed25519::PrivateKey) -> TransactionDocument {
        unimplemented!()
    }
}

It's works when I'm using it after an .iter() but not after a .map(), and says it's not implemented. But std::slice::Iter and std::iter::Map implements Iterator<Item = T> + Clone, so where is the problem ?

Thank you beforehand for you help.

2
  • 1
    Posting a Minimal, Complete, and Verifiable example (MCVE) that demonstrates your problem would help you get better answers. Commented Dec 27, 2017 at 10:30
  • I didn't known. I'll keep it in mind for future questions. Commented Dec 27, 2017 at 12:50

1 Answer 1

2

MCVE for your question could be written as

vec![1,2,3].iter().map(|x| x).to_separated_string("", "")

You were wrong in the following assumption

std::iter::Map implements Iterator<Item = T> + Clone

Trait implementations section for std::iter::Map in the Rust documentation includes

impl<I, F> Clone for Map<I, F> where
    F: Clone,
    I: Clone, 

that is Map implements Clone when the source iterator I and the type of the function F both implement Clone.

Unfortunately, closures don't implement Clone in current stable Rust version 1.22.1. The feature is available in nightly Rust under feature gate clone_closures. Playground link

You can also remove the requirement for Clone by rewriting to_separated_string like that

fn to_separated_string(self, line_prefix: &str, separator: &str) -> String {
    self.fold((true, "".to_string()), |(first, acc), line| {
        (
            false,
            format!(
                "{}{}{}{}",
                acc,
                if first { "" } else { separator },
                line_prefix,
                line
            ),
        )
    }).1
}

Playground link

Sign up to request clarification or add additional context in comments.

2 Comments

great answer. Adding a reference to the copy/clone closures RFC
I didn't thought of the closure. Thanks for the working code, not though either of this tuple setup. Very elegant, I'll try to use this kind of things later. Do you know a place to find those kind of tricks ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.