Возвращает изменяемую ссылку, которая находится за неизменной ссылкой, переданной функции - PullRequest
0 голосов
/ 06 сентября 2018

Как обрабатывается изменяемая ссылка, которая находится за неизменяемой ссылкой, переданной в качестве аргумента функции?

struct Foo { i: i32 }

struct Bar<'b> {
    f: &'b mut Foo
}

impl<'a: 'b, 'b> Bar<'b> {
    fn func(&'a self) -> &'b mut Foo {
         self.f
    }
}

fn main() {
    let mut foo = Foo { i: 1 };

    let bar = Bar { f: &mut foo };
    bar.func(); 
}

выдает следующую ошибку:

error[E0389]: cannot borrow data mutably in a `&` reference
 --> src/main.rs:9:14
  |
8 |         fn func(&'a self) -> &'b mut Foo {
  |                 -------- use `&'a mut self` here to make mutable
9 |              self.f
  |              ^^^^^^ assignment into an immutable reference

Я (вроде) понимаю, что здесь пытается предотвратить компилятор. Но меня смущает сообщение об ошибке assignment into an immutable reference. Что именно присваивается в self (или внутри self.f?)?

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

fn main() {
    let mut foo = Foo { i: 1 };

    let bar = Bar { f: &mut foo };
    let pbar = &bar;

    pbar.f.i = 2; // assignment into an immutable reference
}

В первом примере он пытается переместить изменяемую ссылку f из self (поскольку &mut не является Copy типом), рассматривая его как мутацию внутри неизменяемой ссылки self отсюда и сообщение об ошибке assignment into an immutable reference?

1 Ответ

0 голосов
/ 06 сентября 2018

Вы не можете создать изменяемую ссылку из неизменной. Это означает, что вам нужно изменить &self на &mut self:

impl<'a: 'b, 'b> Bar<'b> {
    fn func(&'a mut self) -> &'b mut Foo {
         self.f
    }
}

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

let mut bar = Bar { f: &mut foo };
bar.func(); 

Что именно присваивается себе (или внутри self.x?)?

Сообщение об ошибке может быть немного не так. В вашем коде нет присваивания, но вы возвращаете ссылку mutable . Единственная дополнительная вещь, которую разрешает сделать изменяемая ссылка, - это присвоить self.f или self.f.i.

Определенно, это сообщение об ошибке можно улучшить, но оно содержит подсказку, чтобы сделать изменяемым &'a self для решения проблемы.

Теперь ваш оригинальный вопрос:

Как обрабатывается изменяемая ссылка, которая находится за неизменяемой ссылкой, переданной в качестве аргумента функции?

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

use std::cell::RefCell;
use std::ops::DerefMut;

struct Foo { i: i32 }

struct Bar<'b> {
    // store the data in a RefCell for interior mutability
    f: &'b RefCell<Foo>
}

impl<'a: 'b, 'b> Bar<'b> {
    // Return a RefMut smart pointer instead of mutable ref, but hide the implementation,
    // just exposing it as something that can be mutably dereferenced as a Foo
    fn func(&'a self) -> impl DerefMut<Target = Foo> + 'b {
         self.f.borrow_mut()
    }
}

fn main() {
    let foo = RefCell::new(Foo { i: 1 });
    let bar = Bar { f: &foo };

    let mut f = bar.func(); 
    f.i = 3;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...