Почему изменение значения изменяемой ссылки через необработанный указатель не нарушает правила псевдонимов Rust? - PullRequest
0 голосов
/ 28 сентября 2018

Я не очень хорошо понимаю правила псевдонимов Rust (и из того, что я слышал, они не определены полностью), но у меня возникают проблемы с пониманием, что делает этот пример кода в документации std::slice все в порядке.Я повторю это здесь:

let x = &mut [1, 2, 4];
let x_ptr = x.as_mut_ptr();

unsafe {
    for i in 0..x.len() {
        *x_ptr.offset(i as isize) += 2;
    }
}
assert_eq!(x, &[3, 4, 6]);

Проблема, которую я вижу здесь, заключается в том, что компилятор может считать, что x, являясь ссылкой &mut, является уникальным.Содержимое x изменяется через x_ptr, а затем читается обратно через x, и я не вижу причин, почему компилятор не мог просто предположить, что x не был изменен, так как он никогда не изменялсячерез единственную существующую ссылку &mut.

Итак, что мне здесь не хватает?

  • Требуется ли компилятору считать, что *mut T может иметь псевдоним &mut Tдаже если обычно допускается, что &mut T никогда не псевдоним другого &mut T?

  • Действует ли блок unsafe как своего рода барьер псевдонимов, где компилятор предполагает, что код внутри него мог изменить что-либо в области видимости?

  • Этот пример кода не работает?

Если есть некоторыесвоего рода стабильное правило, которое делает этот пример нормальным, что именно?Какова его степень?Насколько мне следует беспокоиться о предположениях о псевдонимах, нарушающих случайные вещи в unsafe Rust-коде?

1 Ответ

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

Отказ от ответственности: пока нет формальной модели памяти. 1

Прежде всего, я хотел бы обратиться к:

Проблема, которую я вижу здесь, состоит в том, что x, являясь ссылкой &mut, может считаться уникальным для компилятора.

Да ... и нет.x можно считать уникальным , если не заимствовано , важное отличие:

fn doit(x: &mut T) {
    let y = &mut *x;
    //  x is re-borrowed at this point.
}

Поэтому в настоящее время я буду работать с предположением, что вывод указателя из x временно "заимствует" x в некотором смысле.

Это все, конечно, бесполезно, если нет формальной модели, и одна из причин того, почему компилятор rustc не слишком агрессивен с псевдонимамиоптимизаций пока нет: пока формальная модель не определена и код не проверен на соответствие, оптимизации должны быть консервативными.

1 Проект RustBelt полностью посвящен созданию формально проверенной моделиМодель памяти для Rust.Последние новости от Ральфа Юнга были о Stacked Borrows модели .


От Ralf (комментарии): ключевой момент в приведенном выше примере заключается в том, чтоперейдите с x на x_ptr и обратно на x снова.Таким образом, x_ptr в определенном смысле является заимствованным объемом.Если использование перейдет на x, x_ptr, обратно к x и обратно к x_ptr, то последним будет неопределенное поведение:

fn main() {
    let x = &mut [1, 2, 4];
    let x_ptr = x.as_mut_ptr(); // x_ptr borrows the right to mutate

    unsafe {
        for i in 0..x.len() {
            *x_ptr.offset(i as isize) += 2; // Fine use of raw pointer.
        }
    }
    assert_eq!(x, &[3, 4, 6]);  // x is back in charge, x_ptr invalidated.

    unsafe { *x_ptr += 1; }     // BÄM! Used no-longer-valid raw pointer.
}
...