Почему я могу позаимствовать отдельные поля из ящика, а не другие типы ссылок? - PullRequest
3 голосов
/ 17 февраля 2020

С помощью простого объекта я могу получить изменяемые ссылки на отдельные поля:

struct MyObject {
    pub a: i32,
    pub b: i32,
}

fn func_1(obj: &mut MyObject) {
    let a = &mut obj.a;
    let b = &mut obj.b;

    *a += 1;
    *b *= 2;
}

Это не работает, если obj является MutexGuard или RefMut:

fn func_3(mtx: &Mutex<MyObject>) {
    let mut obj = mtx.lock().unwrap();
    let a = &mut obj.a;
    let b = &mut obj.b; // fails
    ...
}

fn func_4(rfc: &mut RefCell<MyObject>) {
    let mut obj = rfc.borrow_mut();
    let a = &mut obj.a;
    let b = &mut obj.b; // fails
    ...
}

Обе ошибки:

error[E0499]: cannot borrow `obj` as mutable more than once at a time
  --> src/main.rs:28:18
   |
27 |     let a = &mut obj.a;
   |                  --- first mutable borrow occurs here
28 |     let b = &mut obj.b; // fails
   |                  ^^^ second mutable borrow occurs here

Однако, этот работает , если obj является Box:

fn func_2(obj: &mut Box<MyObject>) {
    let a = &mut obj.a;
    let b = &mut obj.b;

    *a += 1;
    *b *= 2;
}

См. На Rust Playground .

Мой главный вопрос - почему. Почему компилятор знает, что это нормально для Box, но не для остальных? Box особенный?

1 Ответ

4 голосов
/ 17 февраля 2020

Да, это один из способов, которыми Box по-прежнему особенный, несмотря на то, что приложено немало усилий, чтобы он выглядел как любой другой тип.

RefMut реализован в Руст. Причина, по которой вы можете взять ссылки на член, такой как &mut obj.a, заключается в том, что RefMut реализует Deref и DerefMut, а компилятор использует DerefMut, чтобы взять &mut RefMut<'_, MyObject> и превратить его в &mut MyObject для доступа поле. Но компилятор не знает, что заимствование obj.a отличается от заимствования obj.b, потому что реализация DerefMut непрозрачна.

Box отличается, потому что разыменование Box<T> не go - Deref или DerefMut (за исключением общего c контекста). Вместо этого у компилятора есть специальный код, который знает, что такое Box<T> и как разыменовать его, чтобы получить T. Вот как компилятор узнает, что obj.a и obj.b не пересекаются для Box<T>, но не для RefMut<'_, T> или MutexGuard<'_, T>.

Box - единственный тип, который является особенным в этом особым образом.

Другие ссылки о специальности Box

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