How to cache Option Some value and is there some Reset or Clear trait?

I have the following struct:

#[derive(Debug, Clone, Default)]
pub struct Scope {
    pub name: String,
    pub name_for: Option<String>,
    pub type_of_scope: ScopeType,
}

It holds the current iteration scope value. I want to reuse it on the next iteration. Currently, I just recreate it using scope = Default::default();, however, considering that my loop has several millions iterations, it would me more efficient to use something like: scope.clear();. However, I didn't find any standard Clear or Reset traits. Not a big deal, tho, because I can create my own Clear trait, but one item remains unsolved. name_for may contain Some(String) and I do not want to destroy it even if I assigned None to it. I simply want to reuse the last cleared Some value when I push new chars in it . How can I do that?

scope.name_for.map(|string| string.clear())

Setting name_for will drop the string, so the only ways to keep the allocated memory is to

  1. Not use an Option<String>
    a. Treat an empty string as the None case (assuming that makes sense for your use case)
    b. Have a bool to represent if the String is None, then add some methods to produce a suitable type (fn get_name_for(&self) -> Option<&str>).
  2. Move the string to a different field and back when needed.
pub struct Scope {
    pub name: String,
    pub name_for: Option<String>,
    name_for_allocation: String,
    pub type_of_scope: ScopeType,
}

impl Scope {
    fn set_name_for_none(&mut self) {
        self.name_for_allocation = self.name_for.take().unwrap_or_default();
        self.name_for_allocation.clear()
    }

    fn push_name_for(&mut self, new: &str) {
        let mut name_for = self
            .name_for
            .take()
            .unwrap_or_else(|| std::mem::take(&mut self.name_for_allocation));
        name_for.push_str(new);
        self.name_for = Some(name_for);
    }
}

It seems a good suggestion. I tried something like:

#[derive(Debug, PartialEq, Default, Clone)]
pub enum ScopeType {
    #[default]
    SelfImpl,
    TraitFor,
    Trait,
}

pub trait Clear {
    fn clear(&mut self);
}

#[derive(Debug, Default)]
pub struct Scope<'a> {
    pub name: String,
    pub name_for: Option<&'a mut String>,
    pub type_of_scope: ScopeType,
    name_for_cache: String,
}

impl Clear for Scope<'_> {
    fn clear(&mut self) {
        self.name.clear();
        self.name_for.as_mut().map(|string| string.clear());
        self.type_of_scope = Default::default();
        self.name_for = None;
    }
}
fn main() {
    let mut scope: Scope = Default::default();
    scope.name.push('A');
    scope.name.push('b');
    scope.name_for = Some(&mut scope.name_for_cache);
    if let Some(ref mut name_for) = scope.name_for {
        name_for.push('C');
    }
    println!("scope {} {:?}", scope.name, scope.name_for.unwrap());
    scope.clear();
    scope.name.push('E');
    scope.name.push('f');
    println!("scope {} {:?}", scope.name, scope.name_for.unwrap());
}

However got stuck on the second mutual borrow. Your solution seems to be free from the problem.

Rust doesn't meaningfully support having a reference to a field stored in another field (a "self-referencial struct").

I moved the allocation between fields so no borrowing needed. One of subtle things is that default/empty string doesn't need to allocate, so the std::mem::take replaces the name_for_allocation with an empty string but doesn't need to allocate for that new empty string.

If you can describe or better provide example code of how this structure is used someone maybe able help find the best solution. Another option If your strings are very short, then something like tinystr or smallstr can avoid any allocation.

Is there a particular reason this has to be a trait? Being able to clear an arbitrary generic type doesn't seem all that useful... and in your given details, it seems you're only using it for Scope anyways.

1 Like

Currently, I am working to implement the phase one of the functionality. There is no implementation yet besides of the code below:

#[derive(Debug, PartialEq, Default, Clone)]
pub enum ScopeType {
    #[default]
    SelfImpl,
    TraitFor,
    Trait,
}

pub trait Clear {
    fn clear(&mut self);
}

#[derive(Debug, Default)]
pub struct Scope<'a> {
    pub name: String,
    pub name_for: Option<&'a mut String>,
    pub type_of_scope: ScopeType,
    //name_for_cache: String,
}

impl Clear for Scope<'_> {
    fn clear(&mut self) {
        self.name.clear();
        self.name_for.as_mut().map(|string| string.clear());
        self.name_for = None;
        self.type_of_scope = Default::default();
    }
}

fn main() {
    let mut scope: Scope = Default::default();
    let mut name_for_cache = String::new();
    //let mut name_for_cache2 = String::new();
    scope.name.push_str("Ab");
    scope.name.push('b');

    scope.name_for = Some(&mut name_for_cache);
    if let Some(ref mut name_for) = scope.name_for {
        name_for.push('C');
        name_for.push('d');
    }
    if let Some(ref mut name_for) = scope.name_for {
        name_for.push_str("Zx");
    }

    println!(
        "scope {} {:?}",
        scope.name,
        scope.name_for.as_ref().unwrap()
    );
    scope.clear();
    scope.name.push('E');
    scope.name.push('f');

    scope.name_for = Some(&mut name_for_cache);

    //name_for_cache.push('y');
    println!(
        "scope {} {:?}",
        scope.name,
        //scope.name,
        scope.name_for.as_ref(),
        //&name_for_cache
    );
}

I do not understand why I am getting the error:

error[E0499]: cannot borrow `name_for_cache` as mutable more than once at a time
  --> /media/exhdd/Dev/modu/trait1/test.rs:82:27
   |
64 |     scope.name_for = Some(&mut name_for_cache);
   |                           ------------------- first mutable borrow occurs here
...
82 |     scope.name_for = Some(&mut name_for_cache);
   |     ----------------------^^^^^^^^^^^^^^^^^^^-
   |     |                     |
   |     |                     second mutable borrow occurs here
   |     first borrow later used here

error: aborting due to 1 previous error

In my understanding, the mutable borrow should be dropped after clear. Since, Rust borrow checker works at compilation time, it may not detect clear. But in the case, second Some assignment should drop the previous one, however it doesn't happen. Why? More likely, I will end with a work around as (String,bool) instead of Option<String>. Anyway, if I missed something, you can educate me. Regarding that Rust identifiers limited to 64 bytes, and I can use just 64 bytes array instead of the dynamic string. The scanner, I work on, should work for any arbitrary language including those that have no length limitation of identifiers.

Not at all. I am open to your suggestions.

The lifetime in Scope<'a> is part of the type of the scope instance. So then scope's name_for field is borrowed for 'a and this cannot change. So it cannot change by calling scope.clear().

And since scope is used afterwards:

    scope.name_for = Some(&mut name_for_cache);
//. ^^^^^ here

    //name_for_cache.push('y');
    println!(
        "scope {} {:?}",
        scope.name,
//.     ^^^^^ also here
        //scope.name,
        scope.name_for.as_ref(),
//.     ^^^^^ also here
        //&name_for_cache
    );

the lifetime must extend up to the last use.

But this lifetime is also the lifetime of the borrow. So the compiler cannot make the borrow end at the call to scope.clear().

Would it make sense for your use case to not redefine name_for as None?

impl Clear for Scope<'_> {
    fn clear(&mut self) {
        self.name.clear();
        self.name_for.as_mut().map(|string| string.clear());
        // self.name_for = None;
        self.type_of_scope = Default::default();
    }
}

This way you don't have to borrow your cache a second time. But then, why not put directly an owned empty string in Scope?

It's generally better to put methods into an inherent impl block rather than a trait, unless you're trying to abstract over several types with that by behavior. That's what traits are for, what they do; they describe a certain set of behaviors that multiple types share.

1 Like

Rust programmers are the smartest people I have ever met. You are among of them.