Fail to return something with an enum in &Rc<RefCell> due to "cannot return value referencing local variable"

The source code:

use std::{cell::RefCell, rc::Rc};

struct Group {
  children: Vec<Rc<RefCell<Token>>>,
}
enum Extra {
  Single,
  Group(Group),
}
struct Token {
  value: usize,
  extra: Extra,
}
struct Status {
  last: Option<Rc<RefCell<Token>>>,
}

fn get_prev_token(status: &Status) -> Option<&Rc<RefCell<Token>>> {
  // get the last child in status.last
  if let Some(last) = &status.last {
    let last = last.borrow();
    if let Extra::Group(group) = &last.extra {
      if let Some(last_child) = group.children.last() {
        return Some(last_child);
      }
    }
  }
  return None;
}

Playground

The background is:

  1. There is a file-folder-like nested struct Token which has an enum extra to contain the children as a Group.
  2. They are all stored as Rc for further multi-ownership x mutable requirements.
  3. I'm trying to write a fn get_prev_token() to get the last child of a group.

Then I met the error:

error[E0515]: cannot return value referencing local variable `last`
  --> src/lib.rs:24:16
   |
22 |     if let Extra::Group(group) = &last.extra {
   |                                   ---- `last` is borrowed here
23 |       if let Some(last_child) = group.children.last() {
24 |         return Some(last_child);
   |                ^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

For more information about this error, try `rustc --explain E0515`.

I understand it's because I use &last.extra to match the enum which makes it as a local variable. But I have no idea to walk around.

Anyone has an idea to solve this?

Thanks.

You need to clone the Rc instead of returning a reference to it.

RefCell will never let you keep a bare reference to its contents, because it has to run a destructor when the loan of its contents ends (its Ref handle goes out of scope), and Rust's references by definition can't have any destructors.

7 Likes

Rc is already a smart pointer, so returning a reference to it is a bit redundant. Cloning an Rc is inexpensive, as it just increases the strong count of the Rc. So I would just clone the Rc:

use std::{cell::RefCell, rc::Rc};

struct Group {
  children: Vec<Rc<RefCell<Token>>>,
}

enum Extra {
  Single,
  Group(Group),
}

struct Token {
  value: usize,
  extra: Extra,
}

struct Status {
  last: Option<Rc<RefCell<Token>>>,
}

fn get_prev_token(status: &Status) -> Option<Rc<RefCell<Token>>> {
  
  // get the last child in status.last
  if let Some(last) = &status.last {
    let last = last.borrow();
    
    if let Extra::Group(group) = &last.extra {
      if let Some(last_child) = group.children.last() {
        return Some(last_child.clone());
      }
    }
  }
  
  return None;
}

Playground.

5 Likes

It works. Thanks both! @jofas @kornel

1 Like

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.