4ntoine
4ntoine

Reputation: 20422

Rust: ".. does not live long enough"

Here is the code (with a Rust beginner difficulty):

use std::cell::RefCell;
use std::collections::HashMap;

pub trait TIndex<'a> {
    // cut
}

pub struct HashMapIndex<'a> {
    pub filter_by_keyword: HashMap<String, &'a str> // simplified (and ugly)
}

impl<'a> TIndex<'a> for HashMapIndex<'a> {
    // cut
}

pub struct Matcher<'a> {
    pub index: &'a RefCell<Box<dyn TIndex<'a> + 'a>>
}

impl<'a> Matcher<'a> {
    pub fn new(index: &'a RefCell<Box<dyn TIndex<'a> + 'a>>) -> Self {
        Matcher {
            index
        }
    }
}

pub fn main() {
    let index = HashMapIndex {
        filter_by_keyword: HashMap::new()
    };
    let boxed_index: Box<dyn TIndex> = Box::new(index);
    let refcell = RefCell::from(boxed_index);
    let mut _matcher = Matcher::new(
        &refcell // `refcell` does not live long enough
    );
}

playground

I'm not sure i understand what is wrong (but something is definitely). 'a is a main() function scope here, index and refcell live until main() exits. matcher accepts a reference to RefCell that lives at least 'a and references to a box that lives at least 'a and point to a TIndex trait object that lives at least 'a and whose internals live at least 'a.

How should it be changed (and what's wrong in lifetimes declarations here)?

PS. I'm getting a compiler hint (at main() closing }):

    }
    | -
    | |
    | `refcell` dropped here while still borrowed
    | borrow might be used here, when `refcell` is dropped and runs the destructor for type `RefCell<Box<dyn TIndex<'_>>>`

not sure i understand it as refcell is passed by reference (borrowed by _matcher).

Upvotes: 1

Views: 165

Answers (1)

Masklinn
Masklinn

Reputation: 42492

not sure i understand it as refcell is passed by reference (borrowed by _matcher).

The problem is that you're defining Matcher such that the lifetimes of the RefCell and the lifetime of the contents of the RefCell must be the same.

This means you're telling rustc the RefCell must live as long what it contains, meaning once you've put the RefCell inside the Matcher... your program can't work anymore, because a container can't rightly outlive its contents.

You need to split your lifetimes so rustc knows how they nest, at the very least you need to give a different lifetime to the &RefCell and its contents -- and tell rustc that the contents outlive the &RefCell:

pub struct Matcher<'a, 'b> {
    pub index: &'a RefCell<Box<dyn TIndex<'b> + 'b>>
}

impl<'a, 'b: 'a> Matcher<'a, 'b> {
    pub fn new(index: &'a RefCell<Box<dyn TIndex<'b> + 'b>>) -> Self {
        Matcher {
            index
        }
    }
}

It might also be a good idea to split the lifetime parameter of the TIndex and the lifetime bound of the TIndex (the contents of the TIndex should outlive it), but it doesn't seem necessary for the repro you've posted here.

Upvotes: 2

Related Questions