Pawan Kumar
Pawan Kumar

Reputation: 1533

How to resolve "creates a temporary variable which is freed while still in use"?

I am trying to implement Debug for my custom List<T> using a while loop.

use std::cell::RefCell;
use std::rc::Rc;

type Link<T> = Option<Rc<RefCell<Node<T>>>>;
struct Node<T> {
    val: T,
    next: Link<T>,
}
struct List<T> {
    head: Link<T>,
    tail: Link<T>,
    len: usize,
}

use std::fmt;
impl<T: fmt::Debug> fmt::Debug for List<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut temp = &self.head;
        while let Some(r) = temp {
            write!(f, "{:?} -> ", r.borrow().val);
            temp = &r.borrow().next; // ERROR
        }
        write!(f, "[]")
    }
}
error[E0716]: temporary value dropped while borrowed
  --> src/lib.rs:21:21
   |
21 |             temp = &r.borrow().next; // ERROR
   |                     ^^^^^^^^^^     -
   |                     |              |
   |                     |              temporary value is freed at the end of this statement
   |                     |              ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::cell::Ref<'_, Node<T>>`
   |                     creates a temporary which is freed while still in use
   |                     a temporary with access to the borrow is created here ...
   |
   = note: consider using a `let` binding to create a longer lived value

I know that I am taking a reference of a temporary value r, which is bad. Is there an elegant and idiomatic solution around this problem?

Are there any borrow patterns for Rust code followed by Rustaceans? I know one such pattern is std::mem::replace to take ownership of a value, which works only when we have &mut T, some call it Indiana Jones Pattern 🙂

Upvotes: 7

Views: 3536

Answers (1)

Pawan Kumar
Pawan Kumar

Reputation: 1533

I asked the same question on the Rust User's Forum and got an answer from alice:

impl<T: fmt::Debug> fmt::Debug for List<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut temp = self.head.clone();
        while let Some(r) = temp {
            write!(f, "{:?} -> ", r.borrow().val);
            temp = r.borrow().next.clone();
        }
        write!(f, "[]")
    }
}

The reason for this is that borrow() returns a guard with a destructor, and you can only access references into the RefCell while that guard is still active. So without cloning an Rc, you would need to keep all the guards from each iteration around, but the loop destroys the previous guard when advancing to the next iteration. An Rc clone lets you access the next node without going through the guard, hence sidestepping the issue.

Upvotes: 2

Related Questions