Rust borrow mutable self inside match expression

后端 未结 1 568
温柔的废话
温柔的废话 2020-12-21 05:35

I have a problem with self borrowing in a match expression:

fn add_once(&\'t mut self, p_name: &\'t str) -> Box

        
相关标签:
1条回答
  • 2020-12-21 06:22

    Let's fix the design issues first. The main issue is lifetime conflation:

    struct Top<'t> {
        list: Vec<Element<'t>>,
    }
    
    impl<'t> Top<'t> {
        fn add(&'t mut self, p_name: &'t str) -> Box<Element>;
        fn add_once(&'t mut self, p_name: &'t str) -> Box<Element>;
    }
    

    You assert that self should live at least as long as 't, which is itself the lifetime bound of the references it will contain. It is not actually what you need, self should live less than 't to guarantee that any &'t is still alive and kicking until self dies.

    If we change this, we can painlessly return references to Element:

    impl<'t> Top<'t> {
        fn add<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t>;
        fn add_once<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t>;
    }
    

    Note that the lifetime 'a of the reference to Element is different (and actually will be shorter) than the lifetime 't of its contained reference.

    With that out of the way, this should fix the functions:

    fn position(&self, p_name: &str) -> Option<usize> {
        self.list.iter().position(|e| e.name == p_name)
    }
    
    fn add<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t> {
        self.list.push(Element::new(p_name));
        &self.list[self.list.len() - 1]
    }
    
    fn add_once<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t> {
        if let Some(p) = self.position(p_name) {
            return &self.list[p];
        }
        self.add(p_name)
    }
    

    And position can be reused for get:

    fn get<'a>(&'a self, p_name: &str) -> Option<&'a Element<'t>> {
        self.position(p_name).map(|pos| &self.list[pos])
    }
    

    I would expect the following to work in a perfect world:

    fn add_once<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t> {
        match self.get(p_name) {
            Some(x) => x,
            None => self.add(p_name)
        }
    }
    

    However, I recall a discussion in which it was determined that the borrow-checker was not lax enough: the scope of the borrow induced by self.get is computed to be the entire match expression even though in the None branch the temporary cannot be accessed.

    This should be fixed once "Non-Lexical Lifetimes" are incorporated in Rust, which is a work in progress.

    0 讨论(0)
提交回复
热议问题