Simple question()

I have struct, want to save in it

Some(Vec<String>)

(L field)
Have method, want to pass in it &self, but
Check, if this field L not empty=> have a reference to it.
If it is empty, create new, but Cell pretend on Copy trait, and how i can this realize?)

I don't quite understand what you mean. Could you provide a more detailed example of what you're trying to do?

Ok: for example

pub struct Smth{
//...
pub your_languages: Option<Vec<String>>}
impl<'a> Smth<'a>{
{

       fn new_lang_vec(&self, cap: Option<usize>) -> Option<Vec<String>>
       {
        const DEFAULT_VEC_CAP: u8 = 12;
        if let Some(i) = cap {
            Some(Vec::with_capacity(i as usize))
       }
       else{Some(Vec::with_capacity(DEFAULT_VEC_CAP as usize))}//or Vec::new()
   }
fn say_smth<'b>(&self, available_languages: HashMap<String,String>){
    //1.   
       let your_ls: &Vec<String>  = self.your_languages.as_ref() // either it will be moving value and error
            .unwrap_or_else(||  {   
                self.your_languages= self.new_lang_vec(None);
                &self.your_languages.unwrap()});//.unwrap_or_else(||  Box::new(self.new_lang_vec(Some(None))))
//.... Other stuff... 

Question straitforward: need only reference, but initialize it if there are nothing, Cell need copy as i say)

For improved readability, I recommend using rustfmt on your code before asking a question, and making sure you aren’t messing up your braces (“{” and “}”) or other simple errors.

So your code example looks like this

use std::collections::HashMap;

pub struct Smth {
    //...
    pub your_languages: Option<Vec<String>>,
}
impl Smth {
    fn new_lang_vec(&self, cap: Option<usize>) -> Option<Vec<String>> {
        const DEFAULT_VEC_CAP: u8 = 12;
        if let Some(i) = cap {
            Some(Vec::with_capacity(i as usize))
        } else {
            Some(Vec::with_capacity(DEFAULT_VEC_CAP as usize)) //or Vec::new()
        }
    }
    fn say_smth(&self, available_languages: HashMap<String, String>) {
        //1.
        let your_ls: &Vec<String> = self
            .your_languages
            .as_ref() // either it will be moving value and error
            .unwrap_or_else(|| {
                self.your_languages = self.new_lang_vec(None);
                &self.your_languages.unwrap()
            }); //.unwrap_or_else(||  Box::new(self.new_lang_vec(Some(None))))

        //.... Other stuff...
    }
}

So in case you only ever want to replace an uninitialized (i.e. “None”-valued) field with an initialized one, you could use consider using something like OnceCell.

E.g.

use std::collections::HashMap;
use once_cell::unsync::OnceCell;

pub struct Smth {
    //...
    pub your_languages: OnceCell<Vec<String>>,
}
impl Smth {
    fn new_lang_vec(&self, cap: Option<usize>) -> Vec<String> {
        const DEFAULT_VEC_CAP: u8 = 12;
        if let Some(i) = cap {
            Vec::with_capacity(i as usize)
        } else {
            Vec::with_capacity(DEFAULT_VEC_CAP as usize) //or Vec::new()
        }
    }
    fn say_smth(&self, available_languages: HashMap<String, String>) {
        //1.
        let your_ls: &Vec<String> = self.your_languages.get_or_init(|| self.new_lang_vec(None));

        //.... Other stuff...
    }
}

You can also simplify new_lang_vec; it doesn’t access/need self and it could use Option::unwrap_or

use std::collections::HashMap;
use once_cell::unsync::OnceCell;

pub struct Smth {
    //...
    pub your_languages: OnceCell<Vec<String>>,
}
impl Smth {
    fn new_lang_vec(cap: Option<usize>) -> Vec<String> {
        const DEFAULT_VEC_CAP: usize = 12;
        Vec::with_capacity(cap.unwrap_or(DEFAULT_VEC_CAP))
    }
    fn say_smth(&self, available_languages: HashMap<String, String>) {
        //1.
        let your_ls: &Vec<String> = self.your_languages.get_or_init(|| Self::new_lang_vec(None));

        //.... Other stuff...
    }
}
2 Likes

I guess you're looking for Option::get_or_insert_with. Edit: this will work only if you take a &mut self in say_smth

You may also consider having new_lang_vec return a Vec<String since it never returns None

1 Like

Okay, but how it is modified?
It is not copiable, for OnceCell it does not matter?
(imply why)

Well, OnceCell doesn’t allow any modification through shared references (i.e. through &self) except for initialization. If you also need to modify the cell’s contents in other ways (without taking &mut self), then OnceCell won’t help. Cell on the other hand does never allow you to get a reference to its inner value (except with &mut self access). You could also make this initialization work with Cell, but if that’s the right thing to do depends on what else you need/want to be able to do from a shared &self reference.

There’s also the question of whether you want to use a Cell or something similar at all. As @SkiFire13 explained above, if you just took &mut self, you could easily work directly with an Option. So you’ll have to ask yourself: Why does say_smth have to work with &self instead of &mut self and what other kinds of access patterns do I need to support thorugh &self?

To give some example code of how Cell or RefCell could be used:

with Cell, taking the approach of Cell::take()ing and putting back at the end

use std::cell::Cell;
use std::collections::HashMap;

pub struct Smth {
    //...
    pub your_languages: Cell<Option<Vec<String>>>,
}
impl Smth {
    fn new_lang_vec(cap: Option<usize>) -> Vec<String> {
        const DEFAULT_VEC_CAP: usize = 12;
        Vec::with_capacity(cap.unwrap_or(DEFAULT_VEC_CAP))
    }
    fn say_smth(&self, available_languages: HashMap<String, String>) {
        //1.
        let mut your_ls = self.your_languages.take();
        
        let your_ls_ref: &Vec<String> = your_ls.get_or_insert_with(|| Self::new_lang_vec(None));

        //.... Other stuff...
        
        // in the end put your_ls back!!
        self.your_languages.set(your_ls);
    }
}

with RefCell, while making sure that a RefMut guard is only created when necessary and for as short as possible (code untested)

use std::cell::RefCell;
use std::collections::HashMap;

pub struct Smth {
    //...
    pub your_languages: RefCell<Option<Vec<String>>>,
}
impl Smth {
    fn new_lang_vec(cap: Option<usize>) -> Vec<String> {
        const DEFAULT_VEC_CAP: usize = 12;
        Vec::with_capacity(cap.unwrap_or(DEFAULT_VEC_CAP))
    }
    fn say_smth(&self, available_languages: HashMap<String, String>) {
        //1.
        if self.your_languages.borrow().is_none() {
            *self.your_languages.borrow_mut() = Some(Self::new_lang_vec(None))
        }
        let your_ls = self.your_languages.borrow();
        let your_ls: &Vec<String> = your_ls.as_ref().unwrap();

        //.... Other stuff...
    }
}

Thanks a lot, above &self is only because there is no need to modifie struct with much fields except this one, but would be more simple only seperate method for this with &mut, so- it's interesting what you had written, especially how this avoid mutability)

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.