Confused about { Rc::clone(&n.borrow() } for RefCell<Rc<T>>

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

#[derive(Debug)]
enum ConsList {
    Val(RefCell<i32>, RefCell<Rc<ConsList>>),
    Nil,
}

impl ConsList {
    fn new() -> Rc<ConsList> {
        Rc::new(ConsList::Nil)
    }
    
    fn new_val(val: i32) -> Rc<ConsList> {
        Rc::new(ConsList::Val(RefCell::new(val), RefCell::new(ConsList::new())))
    }
    
    fn new_val_next(val: i32, next: &Rc<ConsList>) -> Rc<ConsList> {
        Rc::new(
            ConsList::Val(
                RefCell::new(val),
                RefCell::new(Rc::clone(next))
            ) 
       )
    }
}

fn main() {

    let l1 = ConsList::new_val(4);
    let l2 = ConsList::new_val_next(5, &l1);
    let l3 = ConsList::new_val_next(6, &l2);
    let l4 = ConsList::new_val_next(7, &l3);
    
    
    let mut ptr = Rc::clone(&l4); 
    
    while let ConsList::Val(v, n) = &*ptr {
        println!("v: {}", v.borrow());
        // ptr = Rc::clone(&n.borrow());
        ptr = { Rc::clone(&n.borrow()) }
    }
}

(Playground)

Output:

v: 7
v: 6
v: 5
v: 4

Errors:

   Compiling playground v0.0.1 (/playground)
warning: unnecessary braces around assigned value
  --> src/main.rs:42:15
   |
42 |         ptr = { Rc::clone(&n.borrow()) }
   |               ^^                      ^^
   |
   = note: `#[warn(unused_braces)]` on by default
help: remove these braces
   |
42 -         ptr = { Rc::clone(&n.borrow()) }
42 +         ptr = Rc::clone(&n.borrow())
   |

warning: `playground` (bin "playground") generated 1 warning (run `cargo fix --bin "playground"` to apply 1 suggestion)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.64s
     Running `target/debug/playground`

Why does:

ptr = Rc::clone(&n.borrow());

The above does not compile,

But the snippet below:

ptr ={ Rc::clone(&n.borrow()) };

Compiles and works as intended?

I believe it's because when the temporary Ref<'_, _> that gets created by borrow drops at the end of statement, that's considered to be after the assignment. Whereas when you add the braces, the Ref drops before the assignment.

So it's sort of similar to how

        let borrow = n.borrow();
        let tmp = Rc::clone(&borrow);
        ptr = tmp;

doesn't compile because borrow drops after the assignment, but

        let tmp = Rc::clone(&n.borrow());
        ptr = tmp;

compiles because the Ref drops at the end of the let tmp = ... line.

1 Like