(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?)
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
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)