Relrin
Relrin

Reputation: 790

Get string as a value in formatted output

I'm trying to pass the "Tuple lessons" on the Rust by Example website, but I am stuck on the formatted output implementation. I have this code, which prints the passed matrix:

#[derive(Debug)]
struct Matrix{
  data: Vec<Vec<f64>>   // [[...], [...],] 
}

impl fmt::Display for Matrix {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let output_data = self.data
            // [[1, 2], [2, 3]] -> ["1, 2", "2, 3"]
            .into_iter()
            .map(|row| {
                row.into_iter()
                    .map(|value| value.to_string())
                    .collect::<Vec<String>>()
                    .join(", ")
            })
            .collect::<Vec<String>>()
            // ["1, 2", "2, 3"] -> ["(1, 2)", "(2, 3)"]
            .into_iter()
            .map(|string_row| { format!("({})", string_row) })
            // ["(1, 2)", "(2, 3)"] -> "(1, 2),\n(2, 3)"
            .collect::<Vec<String>>()
            .join(",\n");
        write!(f, "{}", output_data)
    }
}

But the compiler prints the next message:

<anon>:21:40: 21:44 error: cannot move out of borrowed content [E0507]
<anon>:21         let output_data = self.data
                                    ^~~~
<anon>:21:40: 21:44 help: see the detailed explanation for E0507
error: aborting due to previous error
playpen: application terminated with error code 101

I've tried to wrap output_data's result into a RefCell, but the complier still prints this error. How can I fix this issue, so that the write! macro works correctly?

Upvotes: 3

Views: 88

Answers (1)

malbarbo
malbarbo

Reputation: 11177

The problem is that into_inter takes the ownership of data, that is, is move out data from self, and that is not allowed (that is what the error says). To iterate in a vector without taking ownership, use iter method:

fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let output_data = self.data
        // [[1, 2], [2, 3]] -> ["1, 2", "2, 3"]
        .iter()
        .map(|row| {
            row.into_iter()
                .map(|value| value.to_string())
                .collect::<Vec<String>>()
                .join(", ")
        })
        .collect::<Vec<String>>()
        // ["1, 2", "2, 3"] -> ["(1, 2)", "(2, 3)"]
        .into_iter()
        .map(|string_row| { format!("({})", string_row) })
        // ["(1, 2)", "(2, 3)"] -> "(1, 2),\n(2, 3)"
        .collect::<Vec<String>>()
        .join(",\n");
    write!(f, "{}", output_data)
}

Take a look at Formatter. It has some methods to help write fmt. Here is a version that does not allocate intermediaries vectors and strings:

fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let mut sep = "";
    for line in &self.data { // this is like: for line in self.data.iter()
        try!(f.write_str(sep));
        let mut d = f.debug_tuple("");
        for row in line {
            d.field(row);
        }
        try!(d.finish());
        sep = ",\n";
    }
    Ok(())
}

Upvotes: 5

Related Questions