[Solved] Should i clone to avoid borrow check?

#1

I am trying to loop over a vec of struct LogField reading from json.
Borrow check complains the following at the return statement.
use of moved value: logdef
value used here after move
note: move occurs because logdef.fields has type std::vec::Vec<LogField> , which does not implement the Copy traitrustc(E0382)

i am able to solve this by cloning: for f in logdef.fields.clone()
Is that the correct thing to do here? Would cloning be expensive for a large vector of structure here?

Thanks,
bosscar

let logdef = serde_json::from_str::<LogDoc>(&documents_res.get_data()[0].to_string()).unwrap();
let mut header = String::new();
for f in logdef.fields {
    if f.name != None {
        header += &format!("{},", f.name.unwrap());
    }
}
return (logdef, header);
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
struct LogField {
    name: Option<String>,
    bits: u32,
    skip: Option<bool>,
    signed: Option<bool>,
    expression: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Default, Clone)]
struct LogDoc {
    _id: String,
    _rev: String,
    active: bool,
    log_packet: u32,
    log_version: u32,
    log_size_bytes: usize,
    log_description: String,
    fields: Vec<LogField>,
}
0 Likes

#2

I’m new to rust myself and there likely is a better answer, but I believe you could do this:

    for f in &logdef.fields {
        if f.name != None {
            header += &format!("{},", f.name.clone().unwrap());
        }
    }

This would borrow the logdef.fields for the loop, instead of moving it; but then you need to clone the field names; since those are moved/consumed when you unwrap the option.

I checked it compiles, but I didn’t verify it’s correct.

Edit:
Apparently per this stackoverflow you can get around the fact that unwrap consumes/moves the content of the Option, by doing this:

    let logdef = serde_json::from_str::<LogDoc>(my_string).unwrap();
    let mut header = String::new();
    for f in &logdef.fields {
        if f.name != None {
            header += &format!("{},", f.name.as_ref().unwrap());
        }
    }
    return (logdef, header);

Again, I checked this compiles but not that it’s correct and works.

1 Like

#3

Thank you @tonygrue this works perfectly !!! :clap:

0 Likes

#4

You can eliminate the unwrap by using pattern matching

let logdef = serde_json::from_str::<LogDoc>(my_string).unwrap();
let mut header = String::new();
for f in &logdef.fields {
    if let Some(name) = f.name {
        header += &format!("{},", name);
    }
}
return (logdef, header);
1 Like

#5

If you’re in a tight loop, you could also avoid using format! in every loop iteration, which allocates a new String every time.

use std::fmt::Write;
//...
// same as: header += format!("{},", name);
write!(header, "{},", name).expect("formatting returned error");

In this case, because name is a string, you could just use header += name; header += ","; but write!(...) is more general and anything you can write with format!, you can write with write!.

By the way, expect(message) is like unwrap() but also outputs message on failure.

2 Likes

#6

Thank you @tspiteri this is a nice lesson for me… changing format! to write! speed up my program significantly!

0 Likes