Rust одалживает изменчивое self внутри выражения match

rust

1791 просмотра

1 ответ

У меня проблема с selfзаимствованием в matchвыражении:

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

Сигнатура get()и add()функции:

fn get(&self, p_name: &str) -> Option<&Element>
fn add(&'t mut self, p_name: &'t str) -> Box<Element>

Компилятор отказывается от этого кода:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/main.rs:38:21
   |
36 |         match self.get(p_name) {
   |               ---- immutable borrow occurs here
37 |             Some(x) => Box::new(*x),
38 |             None => self.add(p_name),
   |                     ^^^^ mutable borrow occurs here
39 |         }
40 |     }
   |     - immutable borrow ends here

Я понимаю, но я не понимаю, как я могу переписать matchвыражение.

В связанном вопросе это было решено путем matchвозврата значения и последующего вызова функции. Однако здесь это не работает, поскольку смысл условного выражения состоит не в выборе значения, а в выборочном выполнении действия.

Полный пример кода ниже:

struct Element<'e> {
    name: &'e str, 
}

impl<'e> Element<'e> {
    fn new(p_name: &str) -> Element {
        Element { name: p_name }
    }
}

struct Top<'t> {
    list: Vec<Element<'t>>,
}

impl<'t> Top<'t> {
    fn new() -> Top<'t> {
        Top { list: vec![] }
    }

    fn get(&self, p_name: &str) -> Option<&Element> {
        for element in self.list.iter() {
            if element.name == p_name {
                return Some(element);
            }
        }
        None
    }

    fn add(&'t mut self, p_name: &'t str) -> Box<Element> {
        let new_element = Box::new(Element::new(p_name));
        self.list.push(*new_element);
        return new_element;
    }

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

fn main() {
    let mut t = Top::new();
    let plop1 = t.add_once("plop1");
    let plop2 = t.add_once("plop1");
}
Автор: Tifauv' Источник Размещён: 13.11.2019 11:34

Ответы (1)


3 плюса

Давайте сначала исправим проблемы с дизайном. Основная проблема - пожизненное соединение:

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>;
}

Вы утверждаете, что selfдолжны жить по крайней мере столько же, сколько 'tи само время жизни ссылок, которые оно будет содержать. Это на самом деле не то, что вам нужно, selfнужно жить меньше, чем 'tгарантировать, что кто-то &'tеще жив и пинать до самой selfсмерти.

Если мы изменим это, мы можем безболезненно вернуть ссылки на 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>;
}

Обратите внимание, что время жизни 'aссылки на Elementотличается (и на самом деле будет короче), чем время жизни 'tсодержащейся ссылки.

С этим из пути, это должно исправить функции:

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)
}

И positionможет быть повторно использован для get:

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

Я ожидаю, что в идеальном мире будут работать следующие:

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)
    }
}

Однако я вспоминаю дискуссию, в которой было определено, что средство проверки заимствований не было достаточно слабым: объем вызванного заимствования self.getрассчитывается как целое matchвыражение, хотя в Noneветке временный доступ недоступен.

Это должно быть исправлено, как только «Нелексические времена жизни» будут включены в Rust, который находится в стадии разработки.

Автор: Matthieu M. Размещён: 13.11.2014 05:40
Вопросы из категории :
32x32