2

I need to initialize an item (fn init(&mut self) -> Option<&Error>), and use it if there's no errors.

pub fn add(&mut self, mut m: Box<Item>) {
    if let None = m.init() {
        self.items.push(m);
    }
}

This works unless I need to check the error if there's any:

pub fn add(&mut self, mut m: Box<Item>) {
    if let Some(e) = m.init() {
        //process error
    } else {
        self.items.push(m); //won't compile, m is borrowed
    }
}

Fair. Need to use RefCell. However, this

pub fn add(&mut self, mut m: Box<Item>) {
    let rc = RefCell::new(m);

    if let Some(e) = rc.borrow_mut().init() {           
        //process error         
    } else {
        self.items.push(rc.borrow_mut())
    }
}

ends with weird

error: `rc` does not live long enough
        if let Some(e) = rc.borrow_mut().init() {
                     ^~
note: reference must be valid for the destruction scope surrounding block at 75:60...
    pub fn add_module(&mut self, mut m: Box<RuntimeModule>) {
                                                        ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 76:30
        let rc = RefCell::new(m);

I tried nearly everything: plain box, Rc'ed box, RefCell'ed box, Rc'ed RefCell. Tried to adapt this answer to my case. No use.

Complete example:

use std::cell::RefCell;
use std::error::Error;

trait Item {
    fn init(&mut self) -> Option<&Error>;
}

struct ItemImpl {}

impl Item for ItemImpl {
    fn init(&mut self) -> Option<&Error> {
        None
    }
}

//===========================================

struct Storage {
    items: Vec<Box<Item>>,
}

impl Storage {
    fn new() -> Storage {
        Storage{
            items: Vec::new(),
        }
    }

    fn add(&mut self, mut m: Box<Item>) {
        let rc = RefCell::new(m);

        if let Some(e) = rc.borrow_mut().init() {           
            //process error         
        } else {
            self.items.push(*rc.borrow_mut())
        }
    }
}

fn main() {
    let mut s = Storage::new();
    let mut i = Box::new(ItemImpl{});
    s.add(i);
}

(Playground)

UPD: As suggested, this is a "family" of mistakes like I did, it is well explained here. However my case has easier solution.

JDemler
  • 1,246
  • 14
  • 22
snuk182
  • 1,022
  • 1
  • 12
  • 28
  • The `RefCell` will not resolve this issue. This is a know limitation of the borrow checker and there are some questions/answers about this. I pointed one possible duplicate, but I think we should find a definitive answer (if possible). @Shepmaster do you know a better duplicate? – malbarbo Jul 05 '16 at 22:57
  • Possible duplicate of [If let borrow conundrum](http://stackoverflow.com/questions/30243606/if-let-borrow-conundrum) – malbarbo Jul 05 '16 at 22:57
  • @malbarbo I don't think it's exactly the same as that one. It has to do with a returning a reference from a `&mut self` method. That causes the mutable borrow to persist. [example](https://play.rust-lang.org/?gist=5a6c22cd3d19b5b5eb9f3a759ecb84aa) – Shepmaster Jul 05 '16 at 23:05
  • Meanwhile I have found a solution, however it looks utterly ugly to me. https://gist.github.com/7f108112b95e2550691095e34c7cf37f – snuk182 Jul 05 '16 at 23:20
  • 1
    The simplest way would be to use early `return` after processing error. @Shepmaster I think it's actually quite the same, `Option` also borrows from `&mut self`, the lifetime parameter is just hidden by elision. – krdln Jul 05 '16 at 23:28

1 Answers1

3

As krdln suggested, the simplest way to work around this is to return in the if block and thus scope the borrow:

fn add(&mut self, mut m: Box<Item>) {
    if let Some(e) = m.init() {
        //process error
        return;
    } 
    self.items.push(m);
}
JDemler
  • 1,246
  • 14
  • 22