Borrowing and lifecycles - is it problem with &str?

Hi,
I have problem with borrowing.
I have struct with &'a str

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Product<'a> {
    id: Uuid,
    shop: &'a str,
    name: &'a str
}

impl<'a> Product<'a> {
    pub fn id(&self) -> Uuid {
        self.id
    }

    pub fn set_shop(&mut self, shop: &'a str) -> &mut Self {
        self.shop = shop;
        self
    }

    pub fn shop(&self) -> &str {
        self.shop
    }

    pub fn set_name(&mut self, name: &'a str) -> &mut Self {
        self.name = name;
        self
    }

    pub fn name(&self) -> &str {
        self.name
    }
}

I import this to other crate wich has mocked service using it

#[derive(Default, Debug)]
pub struct ProductService<'a> {
    products: Vec<Product<'a>>
}

impl ProductService<'_> {
    pub fn new() -> Self {
        let products = file_helper();

        for product in products.iter() {
            println!("{}", product.id());
        }

        ProductService {
            products
        }
    }

    pub fn get_products(&self) -> &Vec<Product> {
        &self.products
    }
}

fn file_helper() -> Vec<Product<'static>> {
    let data = std::fs::read_to_string("./products.json").expect("Unable to read file");
    let json: Vec<Product> = serde_json::from_str(&data).expect("error parsing");    

    json    
}

The problem is in fn file_helper() which try to deserialize json data. This json file I create earlier by manually creating Products to vector and serialize it with writting to file. Now this file I'm trying to deserialize.
When I try to build this I'm gettint error:

error[E0515]: cannot return value referencing local variable `data`
  --> src/handlers/../services/productservice.rs:65:5
   |
63 |     let json: Vec<Product> = serde_json::from_str(&data).expect("error parsing");    
   |                                                   ----- `data` is borrowed here
64 |
65 |     json
   |     ^^^^ returns a value referencing data owned by the current function

This function I've moved from new() because I had the same probem but moving outside didnt help :frowning:
When I try to create manulaly vector of Products it works, byt from deserialize does't.

How to solve this problem? Should I change &a' str in my Product struct to String? or somtething else?
Thanks for help in advance.

Inline the helper will work. But in the general case, unless you benchmarked and found out creating Strings is the bottleneck, you should just use String.

1 Like

This is almost certainly the correct solution for you. &’a str always refers to data that is owned elsewhere; in the case of your deserialization code, that’s the variable data which contains the unparsed JSON.

In order to keep using &’a str inside the struct Product, you’ll need to store the read file somewhere that lives as long as ’a. Inlining file_helper as @zirconium-n mentions is one way to do this. Another is to give file_helper access to a buffer that will live long enough:

fn file_helper<'a>(data: &'a mut String) -> Vec<Product<'a>> {
    *data = std::fs::read_to_string("./products.json").expect("Unable to read file");
    serde_json::from_str(data.as_str()).expect("error parsing")
}
2 Likes

Thank you guys for claryfication. This is my trainings because I had long pause with rust and I'm starting learn it again.
I'm think that better for me wiil be to store String struct in Product.
Thanks again

I'm changing &str into String. Is it wise to return &String and on other fields &Vec<String>?

pub struct Product {
    id: Uuid,
    shop: String,
    name: String,
    photos: Vec<String>,
}

impl Product {
    pub fn id(&self) -> Uuid {
        self.id
    }

    pub fn set_shop(&mut self, shop: String) -> &mut Self {
        self.shop = shop;
        self
    }

    pub fn shop(&self) -> &String {
        &self.shop
    }

    pub fn set_name(&mut self, name: &str) -> &mut Self {
        self.name = name.to_string();
        self
    }

    pub fn name(&self) -> &String {
        &self.name
    }
    
    pub fn set_photos(&mut self, photos: Vec<&str>) -> &mut Self {
        for photo in photos {
            self.photos.push(photo.to_string());
        }
        self
    }

    pub fn photos(&self) -> &Vec<String> {
        &self.photos
    }
}

Here you should return &str instead.

Here it is better to take a String so you can avoid the clone. That way, the string does not have to be cloned when that isn't necessary, and when a clone is necessary, the caller can do so.

Here you should take a &[...] as argument instead of a Vec. This way the caller can call the method without having to create a Vec just for that purpose.

Here you should return a &[String]. This is like the case with &String.

2 Likes

And probably &[String], instead of &[&str]. Or even impl Iterator<Item = String>.

Or even impl IntoIterator<Item = String>

1 Like

Thank you for your time and hints. I will comply with them.

Hi again,
Now I have payments: Option<Vec<String>>. Is it correct I return it?

pub struct Shop {
    // more fields
    payments: Option<Vec<String>>,
}
impl Shop {
    pub fn set_payments(&mut self, payments: &[&str]) -> &mut Self {
        let mut payments_temp = vec![];
        for payment in payments {
            payments_temp.push(payment.to_string());
        }
        self.payments = Some(payments_temp);
        self
    }

    pub fn add_payment(&mut self, payment: &str) -> &mut Self {
        match &mut self.payments {
            Some(vec) => vec.push(payment.to_string()),
            None => { 
                self.payments = Some(vec![payment.to_string()])
            }
        }
        self
    }

    pub fn payments(&self) -> Option<&Vec<String>> {
        match &self.payments {
            Some(v) => Some(v),
            None => None,
        }
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.