Borrow check trouble due to return from for loop

Hi all, it's been a while since I've encountered a borrow check error that I can't immediately see a fix to, so I'm calling for help. Here is my code (playground link)

use std::any::Any;

pub struct TypeHolder(Vec<Box<dyn Any>>);

impl TypeHolder {
    fn get_mut<T: Any + Default>(&mut self) -> &mut T {
        for x in self.0.iter_mut() {
            if let Some(x) = x.downcast_mut() {
                return x;
            }
        }
        let v: T = Default::default();
        self.0.push(Box::new(v));
        self.0.last_mut().unwrap().downcast_mut().unwrap()
    }
}

Apparently my for loop is holding onto its borrow due to the return statement, even though it doesn't execute that, which prevents me from modifying the Vec in the case where I don't find the type I'm looking for. I could see a solution where I stored an index into the array during the loop, and then looked up the element afterwards, but that seems less efficient than just returning the reference.

Any suggestions? I'll also welcome miscellaneous code-improvement suggestions.

This is a known limitation of the current borrow checker implementation. Your code compiles successfully on nightly when the new borrow checker “Polonius” is enabled.

If workarounds using indices are too expensive, another option is to use raw pointers to bypass the borrow checker for now:

let result = x as *mut T;
return unsafe { &mut *result };

Unfortunately, I don't know of any better workarounds for this particular case.

2 Likes

If you don't mind iterating twice, you can implement a safe version of this by first checking to see if the first condition is met. Its less efficient than an index solution as well, so its not that helpful in this situation. Just showing it since the same line of thinking is sometimes helpful when running into this issue

Playground

use std::any::Any;

pub struct TypeHolder(Vec<Box<dyn Any>>);

impl TypeHolder {
    fn get_mut<T: Any + Default>(&mut self) -> &mut T {
        if self.0.iter_mut().any(|x| x.downcast_mut::<T>().is_some()) {
            for x in self.0.iter_mut() {
                if let Some(x) = x.downcast_mut() {
                    return x;
                }
            }
            unreachable!()
        } else {
            let v: T = Default::default();
            self.0.push(Box::new(v));
            self.0.last_mut().unwrap().downcast_mut().unwrap()
        }
    }
}

Edit:
I guess this would be the index version of what I did for completeness. It reduces the inefficiency to duplicate calls to downcast_mut if on an item that would succeed.

use std::any::Any;

pub struct TypeHolder(Vec<Box<dyn Any>>);

impl TypeHolder {
    fn get_mut<T: Any + Default>(&mut self) -> &mut T {
        if let Some(i) = self.0.iter_mut().position(|x| x.downcast_mut::<T>().is_some()) {
            self.0[i].downcast_mut().unwrap()
        } else {
            let v: T = Default::default();
            self.0.push(Box::new(v));
            self.0.last_mut().unwrap().downcast_mut().unwrap()
        }
    }
}
1 Like

Thanks for the replies. I ended up going with your nice indexed solution, @drewkett .

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.