I read couple of questions in the forum having same subject, but could not get it solved, I also read about Cow but couldn't know how to use it
I know I can get read of all this headache by using String but want to learn more about borrowing and lifetime.
use std::collections::VecDeque;
use std::path::Path;
use serde::{Deserialize};
use std::borrow::*;
#[derive(Debug, Default, Deserialize)] struct NameTemp { first: String, last: String }
#[derive(Debug, Clone)] struct Name<'a> { first: &'a str, last: &'a str }
#[derive(Debug, Clone, Default)] struct Names<'a> { names: VecDeque<Name<'a>> }
impl<'a> Names<'a> {
pub(crate) fn new() -> Self { Default::default() }
pub(crate) fn read(file: &'a str) -> Self {
let mut records: Vec<NameTemp> = Vec::new();
let mut list = Names::new();
let file_path = Path::new(file);
let mut rdr_ins = csv::ReaderBuilder::new()
.has_headers(true)
.delimiter(b',')
.from_path(file_path).unwrap();
for result in rdr_ins.records() {
records.push(result.unwrap().deserialize(None).unwrap());
};
for record in &records {
let name = Name {
first: record.first.as_str(),
last: record.last.as_str()
};
list.names.push_back(name);
};
// Here the error: returns a value referencing data owned by the current function
list
}
}
fn main(){
// let mut names_list = Names::new(); names_list.read("src/names.csv");
let names_list = Names::read("src/names.csv");
println!("names list: {:#?}", names_list.names);
}
but got stuck that list kept owned by the function read and refused to be returned back to as an out put, i got this error:
error[E0515]: cannot return value referencing local variable `records`
--> src\main.rs:43:9
|
32 | for record in &records {
| -------- `records` is borrowed here
...
43 | list
| ^^^^ returns a value referencing data owned by the current function
and because it's a variable, and you're not storing it anywhere, it will be destroyed at the end of its scope. All records will be gone at the end of this function.
So you can't return references to records that are gone. And you can't return both owned struct and a temporary view of that struct together due to self-referential struct limitation of the borrow checker.
Again, don't put temporary scope-limited references in structs that you want to keep and use outside of a single scope.
<'a> is a huge warning sign. It's not for general-purpose structs, but a very limited case of a thin temporary view of another struct. Read it as "it will be unusable outside of its scope, can't be returned beyond that scope, can't exist without an owned counterpart that has been created before it's been created, and will remain alive for longer than that struct". If you don't intend to put all these limitations on a struct, don't make it temporary. Use owned types: String, Arc, Vec.
You can ask the caller for a place to store it. Note the 'a is not needed on file, as you don't store the name in the struct.
fn read(file: &str, records: &'a mut Vec<NameTemp>) -> Self {
let mut list = Names::new();
let file_path = Path::new(file);
let mut rdr_ins = csv::ReaderBuilder::new()
.has_headers(true)
.delimiter(b',')
.from_path(file_path)
.unwrap();
for result in rdr_ins.records() {
records.push(result.unwrap().deserialize(None).unwrap());
}
for record in &*records {
let name = Name {
first: record.first.as_str(),
last: record.last.as_str(),
};
list.names.push_back(name);
}
list
}
Of course, this means the caller has to provide the vector.
fn main() {
let mut records = Vec::new();
let names_list = Names::read("src/names.csv", &mut records);
println!("names list: {:#?}", names_list.names);
}
Additionally it is not possible to modify records while names_list still exists, because names_list contains references into it, which may not be invalidated.
I tried to summarize my understanding as below, hope there is no big misunderstanding:
Caller create a variable to hold the records let mut records = Vec::new();
Caller pass the reference & of this variable as mutable mut to the function let names_list = Names::read(..., &mut records);
Function received the reference of the variable as mutable input fn read(..., records: &'a mut Vec<NameTemp>) -> ... { ... }
Records are pushing into this variable records.push(....);
Each record is read using &*records the objective of using &* here is to access the elements inside the records, as records is the reference of the vector, so we need first to do dereferencing so we can access the elements of the elements using * then pointing to each element using &
We add the new record element (which is existing in the main function, but we updated it through referencing in the read function) to the list that is created inside the read function list.names.push_back(...);
The output is returned to the main function, the list in the read function is dropped, bot records and names_list are updated, where records is the one that is holding the data and names_list is the one that is holding the view of this data
records can not be modified while names_list due to single ownership restriction
Callback is a misleading way to think about this. It's all about scopes. You've had this:
{
call read
{
create records
return list
destroy records
}
use list of records
}
and with the &mut Vec argument it's:
{
create records
call read
{
return list
}
use list of records
destroy records
}
So it's about moving scope of records one level higher, so that it outlives the list. The list is still tied to it, and still can't be returned beyond the scope of records, but at least the scope is a bit larger.