Некоторая путаница в отношении изменчивости ссылки и изменчивости значения, на которое ссылается ссылка

Я знаю, что ссылка на Rust очень похожа на указатель C, и я все время думаю о ссылках на Rust как о указателях C. После некоторых экспериментов и поисков я запуталась.

Я знаком с C и читал В чем разница между размещением «mut» перед именем переменной и после «:»?, что дает следующую таблицу:

// Rust          C/C++
    a: &T     == const T* const a; // can't mutate either
mut a: &T     == const T* a;       // can't mutate what is pointed to
    a: &mut T == T* const a;       // can't mutate pointer
mut a: &mut T == T* a;             // can mutate both

За сообщение проголосовали "за", поэтому я предполагаю, что он правильный.

Я написал следующий код на Rust

fn main() {
    let mut x = 10;
    let x1 = &mut x;
    let x2 = &x1;
    let x3 = &x2;
    ***x3 = 20;
}

в надежде, что он эквивалентен следующему коду C

int main() {
    int x = 10;
    int *const x1 = &x;
    int *const *const x2 = &x1;
    int *const *const *const x3 = &x2;
    ***x3 = 20;
    return 0;
}

Код Rust не компилируется:

error[E0594]: cannot assign to `***x3` which is behind a `&` reference
 --> src/main.rs:6:5
  |
6 |     ***x3 = 20;
  |     ^^^^^^^^^^ cannot assign

Что здесь не так?

Как ни странно, следующий код компилируется!

fn main() {
    let mut x = 10;
    let mut x1 = &mut x;
    let mut x2 = &mut x1;
    let mut x3 = &mut x2;
    ***x3 = 20;
}

Почему следует использовать let mut x1/2/3 вместо просто let x1/2/3? Я думаю о let x1 = &mut x как о постоянном указателе, указывающем на изменяемую переменную x, но в Rust это не похоже. Это сообщение о переполнении стека неточно или я неправильно его понимаю?


person Dev-XYS    schedule 22.09.2020    source источник


Ответы (2)


Есть несколько различий между Rust и C, которые не отображаются в таблице, на которую вы ссылались в своем вопросе.

  1. В Rust изменчивость - это свойство привязки, а не типа.

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

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

let mut x = 10;
let x1 = &mut x;

let x2 = &x1;     // Non mutable reference to x1, ok
let x3 = &x1;     // Another non mutable reference to x1, ok

**x2 = 20;        // uhoh, now I can mutate 'x' via two references ... !
**x3 = 30;

Что касается вашего эквивалента C данного кода Rust - вы не переводили его в соответствии с таблицей. Учти это:

let x2 = &x1;

Из таблицы в приведенном вами ответе:

a: &T == const T* const a; // Can't modify either

В этом случае T будет const int*. Итак, это будет:

const int* const* const x2 = &x1;

Вся ваша программа будет:

int main() {
    // let mut x = 10;
    int x = 10;

    // let x1 = &mut x;
    // a: &mut T == T* const a with T=int
    int* const x1 = &x;

    // let x2 = &x1;
    // a: &T     == const T* const a with T = int* const
    const int* const* const x2 = (const int* const* const) &x1;

    // let x3 = &x2;
    // a: &T     == const T* const a with T = const int* const* const
    const const int* const* const* const x3 = &x2;

    ***x3 = 20;
    return 0;
}

Обратите внимание, что приведение необходимо, чтобы избежать предупреждений при присвоении x2. Это важная подсказка: мы эффективно добавляем константу к объекту, на который указывает объект.

Если вы попытаетесь скомпилировать, то получите:

t.c: In function ‘main’:
t.c:17:11: error: assignment of read-only location ‘***x3’
     ***x3 = 20;
           ^
person harmic    schedule 22.09.2020
comment
Из таблицы в вопросе let x1 = &mut x2; следует переводить как int* const x1 = &x2; (третья строка в таблице). Затем на каждом этапе нужно добавить const перед шрифтом и по одному сзади (первая строка). - person Jmb; 22.09.2020
comment
Я не думаю, что вы хотите иметь const const. Если T равно const int *, то const T *const равно const int *const *const. В этом случае было бы менее запутанно использовать east const: &T == T const *const. - person Tavian Barnes; 22.09.2020
comment
Спасибо @Jmb, исправлено. Вот что происходит из-за попытки написать слишком много тарабарщины на языке Си слишком быстро! - person harmic; 23.09.2020
comment
Спасибо @TavianBarnes. У меня это было правильно в теле программы, а не в верхней части. Я не уверен, что мне нравится восточная версия const больше, чем западная - person harmic; 23.09.2020
comment
Мне тоже не нравится east const, но он допускает простую текстовую замену, такую ​​как T -> T const *const, чтобы делать правильные вещи в большинстве случаев. - person Tavian Barnes; 23.09.2020

В Rust все немного иначе. Знак & означает ссылку на что-то, а знак * означает разыменование чего-либо. Если мне не изменяет память, синтаксис C / C ++ использует символ '- ›' (который является способом разыменования), которого нет в Rust.

Самая сложная часть в Rust - отслеживать, что кто заимствует. Как только вы поймете, как это работает, и поймете, какие типы данных что используют (например, Vec! Использует кучу): вы должны хорошо владеть Rust!

person Charles Jonasson    schedule 22.09.2020