How to completely overwrite an Option mut Vec?

I've seen SO posts for how to mutate one or multiple elements of a vector, but I am trying to store a cache of the last vector I've read. So how can I completely erase the current contents of a vector and fill it full of new content if my cache is type Option<&mut Vec<f32>>?

So for example I want to update it to contain the data of a new vector only if it is Some and currently empty:

let vals = vec![1.0, 2.0];
if cache.is_some() {
      let a: &mut Vec<f32> = cache.unwrap(); // this fails since it causes a move
      if a.is_empty() {

Option type is convenient, if I don't use it it is much more straight forward. But without options I would need twice as many variables to store the state of the cache (Some or None). In this way, the cache is not even allowed to be used if it is None, and is filled for the first time if it is empty or used if it is not empty.

Your code lacks context. But perhaps you mean something like this?

struct Foo<'cache> {
    cache: Option<&'cache mut Vec<f32>>,

impl<'cache> Foo<'cache> {
    fn bar(&mut self, vals: &[f32]) {
        if let Some(ref mut cache) = self.cache {
            if cache.is_empty() {

(However, it's not clear to to me why you don't just have cache: Vec<f32>, or what you meant by erasing the contents since you only do something if it's already empty.)

1 Like

@quinedot this is the kind of brilliant Rust code I need to spend a week understanding before I can even mark it as the answer guilt-free. ref mut, if let, and extend_from_slice vs copy_from are all on my TODO reading list now, thank you!

And I updated clarifying why I use an Optional cache

1 Like

Instead of the ref mut pattern, there's also the possibility of using Option::as_deref_mut here.

I. e., replace cache.unwrap() with cache.as_deref_mut().unwrap() in your original code.

It's also possible to not use ref mut explicitly and rely on "match ergonomics" by doing if let Some(cache) = &mut self.cache in @QuineDot's code.

The code from @QuineDot or the alternative with ergonomics create a &mut &mut Vec<...> which is okay and workable, but if you want to create cache: Vec<...> note that the syntax would be if let Some(&mut ref mut cache) = self. cache. I know, ridiculous, and not really improving readability, but it's an interesting example to study if you're planning to learn about ref patterns anyways.

By the way, using as_mut instead of the as_deref_mut I suggested above will probably also work, the difference again being that as_mut(). unwrap() will give you &mut &mut Vec while as_deref_mut().unwrap() creates &mut Vec.

And of course these methods could be used in if let as well, so if let Some(cache) = self.cache.as_deref_mut() is a good more readable equivalent to if let Some(&mut ref mut cache) = self.cache.

1 Like

@steffahn interesting, but would your suggested syntax enable multiple boolean conditions simultaneously? For instance, checking for cache is Some and empty in the same expression rather than as 2 separate if branches?

You mean avoiding the extra inner if !cache.is_empty() and combining it? I don't see why that's particularly necessary. Perhaps

if let Some(cache) = self.cache.as_deref_mut().filter(|c| !c.is_empty()) {

counts? But I wouldn't necessarily say that that's better than just using an extra if, in terms of clarity.

There's actually a proposal (RFC) to change the language so that you can eventually write something like

if let Some(cache) = self.cache.as_deref_mut() && !cache.is_empty() {

but that's not available in the language right now.

In an unwrap-using approach, you could use matches! macro...

if matches!(&self.cache, Some(cache) if !cache.is_empty()) {

Or with a match, you can use a guard

match &mut self.cache {
    Some(cache) if !cache.is_empty() => cache.extend_from_slice(vals),
    _ => (),

so many ways to do the same thing......


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.