Caballero
Caballero

Reputation: 12101

pattern matching borrowed content issue

So I have this piece of code that reads input.csv, inserts a column in it and writes it to output.csv

extern crate csv;

use std::path::Path;

struct User {
    reference: String,
    email: String,
    firstname: String,
    lastname: String
}

fn main() {

    let mut rdr = csv::Reader::from_file("/tmp/input.csv").unwrap().has_headers(false);
    let mut wtr = csv::Writer::from_file(Path::new("/tmp/output.csv")).unwrap();

    let users = get_users();

    for record in rdr.decode() {

        let rec: Option<Vec<String>> = match record {
            Ok(rec) => Some(rec),
            Err(e) => None
        };

        match rec {
            Some(mut r) => {
                let usr = users.iter().find(|&ur| ur.reference == r[27].to_string());
                match usr {
                    Some(u) => r.insert(1, u.email),
                    None => r.insert(1, "Unknown".to_string())
                }
                wtr.write(r.iter());
            }
            None => {}
        };

    }

}

fn get_users() -> Vec<User> {
    //retrieve users
}

and it's giving me an error:

error: cannot move out of borrowed content
               Some(u) => r.insert(1, u.email),
                                      ^

So I understand it's getting upset about u.email, because r is trying to take ownership of it(?), but how to best handle such a situation?

Upvotes: 4

Views: 98

Answers (1)

Vladimir Matveev
Vladimir Matveev

Reputation: 127791

Here is a slightly simplified portion of your code which demonstrates the problem:

struct User {
    reference: String,
    email: String
}

let users = vec![
    User { reference: "1".into(), email: "[email protected]".into() }, 
    User { reference: "2".into(), email: "[email protected]".into() }
];

let records: Vec<Vec<String>> = vec![
    vec!["1".into()], 
    vec!["2".into()], 
    vec!["3".into()]
];

for mut record in records {
    let usr = users.iter().find(|ur| ur.reference == record[0]);
    match usr {
        Some(u) => record.insert(1, u.email),
        None => record.insert(1, "Unknown".into())
    }
    // do whatever with record
}

usr in let usr here is of type &User, not User, because iter() returns an iterator satisfying Iterator<Item=&User>, and hence find() returns Option<&User>. Consequently, you cannot take a String out of u: &User - you can't move out of a borrowed reference. This is, BTW, an absolutely correct error - if this was allowed, your code would break in a situation with multiple records corresponding to the same user (it would require moving the email out of the same user multiple times).

The most natural way here would be just to use clone():

record.insert(1, u.email.clone())

It would create a copy of the email string contained in the found User, exactly what you need.

Upvotes: 4

Related Questions