Как сообщить компилятору Rust, что заимствование закончилось?

просматривая руководства насчет Rust я столкнулся с проблемой проверки заимствований. Следующий код не компилируется:

struct Car {
    model: String,
}

struct Person<'a> {
    car: Option<&'a Car>,
}

impl<'a> Person<'a> {
    fn new() -> Person<'a> {
        Person { car: None }
    }

    fn buy_car(&mut self, c: &'a Car) {
        // how to say that Person don't borrow the old car any longer?
        self.car = Some(c);
    }
}

fn main() {
    let civic = Car { model: "Honda Civic".to_string() };
    let mut ghibli = Car { model: "Maserati Ghibli".to_string() };
    let mut bob = Person::new();

    bob.buy_car(&ghibli);

    bob.buy_car(&civic);

    // error: cannot borrow `ghibli` as mutable because it is also borrowed as immutable
    let anything = &mut ghibli;
}

Я понимаю, что из-за своей лексической природы программа проверки заимствований Rust не может распознать, что заимствование ghibli уже закончилось.

Но мне очень хотелось бы знать, как решить эту проблему способом Rust? Должен ли я каким-то образом использовать Rc<T> или Box<T>?


person jokeydokey    schedule 17.06.2016    source источник


Ответы (2)


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

Вам нужно использовать Rc<T>, который может выполнять проверки заимствования во время выполнения; или если вам нужен изменяемый доступ, тогда Rc<Cell<T>> или Rc<RefCell<T>>. Тогда, пока вы получаете доступ к нему динамически безопасным способом, все будет хорошо.

person Chris Emerson    schedule 17.06.2016

«Ржавый способ» сообщить контролеру заимствования, что заем закончился, - это ввести новую область видимости:

fn main() {
    let civic = Car{model: "Honda Civic".to_string()};
    let mut ghibli = Car{model: "Maserati Ghibli".to_string()};
    {
        let mut bob = Person::new();

        bob.buy_car(&ghibli);

        bob.buy_car(&civic);
    }
    let anything = &mut ghibli;
}

Однако вы должны понять, что в вашем примере (и, вероятно, в большинстве случаев в любом случае) программа проверки заимствований была права.

Bob позаимствовал ссылку на ghibli. Он по-прежнему присутствует в конце основного метода в его поле car.

person JDemler    schedule 17.06.2016
comment
Нет: bob.buy_car(&civic) перезаписывает поле car. Итак, программа проверки заимствований ошибается ... хотя я думаю, что это правильно, если она будет такой простой. - person Matthieu M.; 17.06.2016
comment
Ах. Да, конечно, вы правы. Должно быть, не заметил этого. - person JDemler; 17.06.2016
comment
@MatthieuM. Насколько я понимаю, программа проверки заимствований не смотрит внутрь методов, а видит только объявления. Подумайте об использовании метода из скомпилированной библиотеки, например, при отсутствии исходного кода. - person hamstergene; 17.06.2016
comment
@hamstergene: ваше понимание более-менее правильное. Средство проверки заимствований заглядывает внутрь _1 _ / _ 2_ (чтобы определить, заимствуют ли они / владеют) не заглядывает внутрь функций, а только использует их подпись. У этой простоты есть два преимущества: четко определенная граница (вы можете изменить внутреннее устройство функции, не нарушая анализа) и относительная простота реализации (моделирование всех путей кода функции было бы ... адом?) . Тем не менее, мы могли бы представить некоторый дополнительный синтаксис для указания семантики замены и тому подобное ... Я не думаю, что это того стоит (слишком редкий случай). - person Matthieu M.; 17.06.2016