Help understanding borrowing and dropping

I'm trying to learn more about borrowing , so I made this:

use std::borrow::Borrow;
use std::collections::VecDeque;

#[derive(Debug)] struct NameString { first: String, last: String }
#[derive(Debug)] struct NameStr<'a> { first: &'a str, last: &'a str }
#[derive(Debug)] struct NameStatic { first: &'static str, last: &'static str }
#[derive(Debug, Default)] struct StrNames<'a> { names: VecDeque<NameStr<'a>> }
#[derive(Debug, Default)] struct StringNames { names: VecDeque<NameString> }

impl<'a> StrNames<'a> {
    pub(crate) fn new() -> Self { Default::default() }
    pub(crate) fn push(&mut self, name_string: NameString) {
        let name = NameStr {                
            first: name_string.first.borrow(),
            last: name_string.last.borrow()
        };
        self.names.push_back(name);
    }
}

impl StringNames {
    pub(crate) fn new() -> Self { Default::default() }
    pub(crate) fn push(&mut self, name_str: NameStr) {
        let name = NameString {               
            first: name_str.first.to_string(),
            last: name_str.last.to_string()
        };
        self.names.push_back(name);
    }
}

fn main(){
    let name_str = NameStr {first: "Hasan", last: "Yousef"};
    let name_string = NameString {first: String::from("Hasan"), last: String::from("Yousef")};

    let mut str_names_list = StrNames::new();
    str_names_list.names.push_back(name_str);

    str_names_list.push(name_string);

   // let mut string_names_list = StringNames::new();
   // string_names_list.names.push_back(name_string);
   // string_names_list.push(name_str);
}

And got the error error about borrowing and life:

error[E0597]: `name_string.first` does not live long enough
  --> src\main.rs:14:20
   |
10 | impl<'a> StrNames<'a> {
   |      -- lifetime `'a` defined here
...
14 |             first: name_string.first.borrow(),
   |                    ^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
17 |         self.names.push_back(name);
   |         -------------------------- argument requires that `name_string.first` is borrowed for `'a`
18 |     }
   |     - `name_string.first` dropped here while still borrowed

error[E0597]: `name_string.last` does not live long enough
  --> src\main.rs:15:19
   |
10 | impl<'a> StrNames<'a> {
   |      -- lifetime `'a` defined here
...
15 |             last: name_string.last.borrow()
   |                   ^^^^^^^^^^^^^^^^ borrowed value does not live long enough
16 |         };
17 |         self.names.push_back(name);
   |         -------------------------- argument requires that `name_string.last` is borrowed for `'a`
18 |     }
   |     - `name_string.last` dropped here while still borrowed

error: aborting due to 2 previous errors

The owner of something either has to clean up after it when finished, or pass it on to some other owner. The problem in your example is that push(&mut self, name_string: NameString) is the owner of NameString and you've pulled a couple of borrowed references out of it, which is fine. But then at the end of the method, you haven't stored the owned NameString anywhere. So it has to get cleaned up at the end of the method on line 18 - which would invalidate those references. And Rust rightly complains about it.

1 Like

To be specific, &str means "this is a reference into some string stored somewhere else", which means that you must ensure that this somewhere else stays alive while the &str exists, as the reference would otherwise be invalidated.

If you don't want your name struct to contain references into some other object, you should use String.

2 Likes

So, how can I store it, I tried using clone instead of borrow as below:

let name = NameStr {
     first: name_string.first.clone().as_ref(),
     last: name_string.last.clone().as_ref()
};

But did not work, as I got:

error[E0716]: temporary value dropped while borrowed
  --> src\main.rs:14:20
   |
10 | impl<'a> StrNames<'a> {
   |      -- lifetime `'a` defined here
...
14 |             first: name_string.first.clone().as_ref(),
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
15 |             last: name_string.last.clone().as_ref()
16 |         };
   |          - temporary value is freed at the end of this statement
17 |         self.names.push_back(name);
   |         -------------------------- argument requires that borrow lasts for `'a`

How can keep the temporary value created longer, or make the borrow lasts for 'a

What if I use &'a str in my code, and later on used another crate that found to be using String do I need to rewrite my code using String or there is a way manage cloning/copying/parsing the String returned from the other crate into &'a str and continue smoothly with my crate?

You can't store a &str in a struct without having the string data be stored somewhere else. That's what the type &str means! You can clone it as much as you want; the data will still not be stored in your NameStr.

To illustrate what would work, see this:

pub fn push(&mut self, name_string: &'a NameString) {
    let name = NameStr {
        first: &name_string.first,
        last: &name_string.last,
    };
    self.names.push_back(name);
}

This takes a reference to a NameString that lives for the 'a lifetime, and then it stores references into that NameString. However, this means that your StrNames now contains a reference to the NameString you gave it a reference to, and that NameString can't go out scope before the StrNames does.

let mut str_names = StrNames::new();
let name_str = ...;
str_names.push(&name_str);

drop(name_str); // destroys the memory with the strings
println!("{:?}", str_names); // Fails to compile ­- the data is destroyed.
1 Like

clone().as_ref() never makes sense. It tries to borrow from a temporary value that has been returned by clone() and not stored. You can't borrow something that isn't stored, and you can't store anything in references.

1 Like

Thanks. Now one line work with me, I think I understood your explanation, but another line did not work, that shake the confidence of my understanding, is it becoming more complicated if the call came from another block like for .. in ..{...} as in the end of the below code?

use std::collections::VecDeque;
use std::path::Path;
use serde::{Deserialize};

#[derive(Debug, Deserialize)] struct NameTemp  { first: String, last: String }
#[derive(Debug)] struct Name<'a> { first: &'a str, last: &'a str }
#[derive(Debug, Default)] struct Names<'a> { names: VecDeque<Name<'a>> }

impl<'a> Names<'a> {
    pub(crate) fn new() -> Self { Default::default() }
    pub(crate) fn push(&mut self, name_temp: &'a NameTemp) {
        let name = Name {
            first: name_temp.first.as_str(),
            last: name_temp.last.as_str()
        };
        self.names.push_back(name);
    }
}

fn main(){
    let mut names_list = Names::new();
    let file_path = Path::new("src/names.csv");
    let mut rdr_ins = csv::ReaderBuilder::new()
        .has_headers(true)
        .delimiter(b',')
        .from_path(file_path).unwrap();

    
    let name_temp = NameTemp {first: String::from("Hasan"), last: String::from("Yousef")};
    // This line is OK
    names_list.push(&name_temp);
    
    
    for result in rdr_ins.records() {
        let record: NameTemp = result.unwrap().deserialize(None).unwrap();

        // This line not OK => names_list.push(&record); borrowed value does not live long enough
        names_list.push(&record);
    };

    println!("{:#?}", names_list);
}

Your code does not compile because record goes out of scope before names_list.

Why in this case, record goes out of scope before names_list while in the few lines above, name_temp did not go out of scope before names_list?

How to fix the issue of the record?

Tried to make it like this:

    let mut record = NameTemp::new();
    for result in rdr_ins.records() {
        record = result.unwrap().deserialize(None).unwrap();

        // This line not OK => names_list.push(&record); borrowed value does not live long enough
        names_list.push(&record);
    };

But got the below:

warning: value assigned to `record` is never read
  --> src\main.rs:37:9
   |
37 |     let mut record = NameTemp::new();
   |         ^^^^^^^^^^
   |
   = note: `#[warn(unused_assignments)]` on by default
   = help: maybe it is overwritten before being read?

error[E0506]: cannot assign to `record` because it is borrowed
  --> src\main.rs:39:9
   |
39 |         record = result.unwrap().deserialize(None).unwrap();
   |         ^^^^^^ assignment to borrowed `record` occurs here
...
42 |         names_list.push(&record);
   |         ----------      ------- borrow of `record` occurs here
   |         |
   |         borrow later used here

In the previous code, record is declared inside the loop body, so it goes out of scope and no longer exists after the loop:

for result in rdr_ins.records() {
        let record = ...
}
// `record` is out of scope now, and its contents have been destroyed.

In the modified code, record exists outside of the loop, but it only holds a single NameTemp. Each time you assign a new value to it, the previous value is dropped and any references to it would be invalidated:

let mut record: NameTemp;
for result in rdr_ins.records() {
    record = ...
}
// `record` now contains whatever value was last assigned to it.

You would need to keep all of the NameTemp values alive, by storing them in a collection like a Vec:

let mut records = Vec::new();
let mut names_list = Names::new();

for result in rdr_ins.records() {
    records.push(result.unwrap().deserialize(None).unwrap());
    let record = records.last().unwrap();
    names_list.push(record);
};

Now you have two collections: records which owns the data, and names_list which just holds borrowed references to it. The references can't outlive the owner, so records must be declared first and destroyed last.

Your struct identifiers are backwards. Your NameTemp holds owned data, which is long-lived, while Name holds only borrows, which are temporary. If you want to store data permanently, you should use owned types. If you want to borrow it temporarily, use references.

1 Like

Thanks, it looks there is dual borrowing issue here?

error[E0502]: cannot borrow `records` as mutable because it is also borrowed as immutable
  --> src\main.rs:39:5
   |
39 |     records.push(result.unwrap().deserialize(None).unwrap());
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
40 |     let record = records.last().unwrap();
   |                  ------- immutable borrow occurs here
41 |     names_list.push(record);
   |     ---------- immutable borrow later used here

error: aborting due to previous error

Ah, yes. Pushing to the Vec isn't possible while something is borrowing from it, because it can invalidate any existing references. You'd need two separate loops, like this:

let mut records = Vec::new();
for result in rdr_ins.records() {
    records.push(result.unwrap().deserialize(None).unwrap());
}

let mut names_list = Names::new();
for record in &records {
    names_list.push(record);
};

But my main point is that you wouldn't normally do this at all. If you just want Names to store a list of names, then make it use owned Strings. Storing one collection of owned strings just to support a second, identical collection of borrowed strings is not a practical use case for borrowing.

1 Like

Thanks a lot @mbrubeck / @alice / @tuffy / @kornel I learned so much from you today, deeply appreciated.

Just putting the updated code below, for future reference:

use std::collections::VecDeque;
use std::path::Path;
use serde::{Deserialize};

#[derive(Debug, Default, Deserialize)] struct NameTemp  { first: String, last: String }
#[derive(Debug)] struct Name<'a> { first: &'a str, last: &'a str }
#[derive(Debug, Default)] struct Names<'a> { names: VecDeque<Name<'a>> }

impl<'a> Names<'a> {
    pub(crate) fn new() -> Self { Default::default() }

    pub(crate) fn push(&mut self, name_temp: &'a NameTemp) {
        let name = Name {
            first: name_temp.first.as_str(),
            last: name_temp.last.as_str()
        };
        self.names.push_back(name);
    }
}

fn main(){
    let mut records: Vec<NameTemp> = Vec::new();
    let mut names_list = Names::new();
    let file_path = Path::new("src/names.csv");
    let mut rdr_ins = csv::ReaderBuilder::new()
        .has_headers(true)
        .delimiter(b',')
        .from_path(file_path).unwrap();

    let name_temp = NameTemp {first: String::from("Hasan"), last: String::from("Yousef")};

    names_list.push(&name_temp);

    for result in rdr_ins.records() {
        records.push(result.unwrap().deserialize(None).unwrap());
    };

    for record in &records {
        names_list.push(record);
    };

    println!("{:#?}", names_list);
}

Cargo:

[dependencies]
csv = "1.1.3"
serde = { version = "1.0.106", features = ["derive"] }

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.