Кажется, я не могу ничего изменить, если в моей цепочке разыменования есть какая-либо неизменная ссылка. Образец:
fn main() {
let mut x = 42;
let y: &mut i32 = &mut x; // first layer
let z: &&mut i32 = &y; // second layer
**z = 100; // Attempt to change `x`, gives compiler error.
println!("Value is: {}", z);
}
Я получаю ошибку компилятора:
error[E0594]: cannot assign to `**z` which is behind a `&` reference
--> src/main.rs:5:5
|
4 | let z: &&mut i32 = &y; // second layer
| -- help: consider changing this to be a mutable reference: `&mut y`
5 | **z = 100; // Attempt to change `x`, gives compiler error.
| ^^^^^^^^^ `z` is a `&` reference, so the data it refers to cannot be written
В некотором смысле это имеет смысл, поскольку в противном случае компилятор не смог бы предотвратить наличие нескольких изменяемых путей доступа к одной и той же переменной.
Однако при взгляде на типы семантика кажется нелогичной:
- Переменная
y
имеет тип&mut i32
, или, говоря простым языком, "изменяемая ссылка на целое число". - Переменная
z
имеет тип&&mut i32
, или, говоря простым языком, "неизменяемая ссылка на изменяемую ссылку на целое число". - Путем разыменования
z
один раз (т.е.*z
) я получу что-то типа&mut i32
, то есть что-то того же типа, что иy
. Однако разыменование этого снова (т.е.**z
) дает мне что-то типаi32
, но мне не разрешено изменять это целое число.
По сути, типы ссылок в некотором смысле лгут мне, поскольку на самом деле они не делают того, что, как они утверждают, делают. Как мне правильно читать типы ссылок в этом случае, или как еще я могу восстановить веру в эту концепцию?
Тестирование с этим образцом:
fn main() {
let mut x = 42;
let y: &mut i32 = &mut x; // first layer
let m: &&mut i32 = &y; // second layer
let z: &&&mut i32 = &m; // third layer
compiler_builtin_deref_first_layer(*z);
}
fn compiler_builtin_deref_first_layer(v: &&mut i32) {
compiler_builtin_deref_second_layer(*v);
}
fn compiler_builtin_deref_second_layer(w: &mut i32) {
println!("Value is: {}", w);
}
Типы параметров этих двух последних функций верны. Если я изменю что-либо из этого, компилятор будет жаловаться на несоответствие типов. Однако, если я скомпилирую пример как есть, я получу эту ошибку:
error[E0596]: cannot borrow `**v` as mutable, as it is behind a `&` reference
Почему-то звонок compiler_builtin_deref_first_layer
кажется нормальным, а звонок compiler_builtin_deref_second_layer
- нет. Ошибка компилятора говорит о **v
, но я вижу только *v
.
&mut y
- person hellow   schedule 06.05.2019z
один раз (т.е.*z
), я получу что-то типа&mut i32
Нет, это будет изменяемый deref, чего нельзя сделать с неизменяемой ссылкой. В лучшем случае вы можете получить&i32
оттуда. - person E_net4 the curator   schedule 06.05.2019&&mut i32
и я выполняю на нем неизменяемый deref, какой тип я получу?&i32
? Каким будет правило вывода этого? - person domin   schedule 06.05.2019Deref
и _ 2_. Документация по каждому признаку объясняет, в каком контексте используется. - person E_net4 the curator   schedule 06.05.2019fn deref(&self) -> &Self::Target
, как мне создать экземпляр этого с моим&&mut i32
? Является ли цельi32
и, следовательно, я верну&i32
? Как так получилось, что целью&&mut i32
являетсяi32
? Разве это не пропуск уровня? - person domin   schedule 06.05.2019<&&mut i32 as Deref>::Target
-&mut i32
, как вы можете видеть из документации, указанной выше. Просмотр определений признакаDeref
здесь не очень помогает, поскольку соответствующий шаг встроен в компилятор, а не в стандартную библиотеку. - person Sven Marnach   schedule 06.05.2019&&mut i32
преобразуется после выполнения одного встроенного в компилятор deref? - person domin   schedule 06.05.2019y
- это эксклюзивная ссылка на целое число, аz
- это общая ссылка на эксклюзивную ссылку на целое число, но вы не можете разыменоватьz
, чтобы получитьy
-подобный эксклюзивный доступ к целому числу, потому что это нарушает собственный договорz
о совместном использовании. - person trentcl   schedule 06.05.2019&&mut i32
шаг за шагом. - person domin   schedule 06.05.2019*z
действительно&mut i32
. Кодlet foo: &mut i32 = *z;
отлично пройдет проверку типов, но программа проверки заимствований будет жаловаться на то, что вы создаете изменяемое заимствование для чего-то, что находится за общей ссылкой. Вам не нужно разбираться в деталях, как работает программа проверки заимствований; достаточно понимать, что он гарантирует, что ничто, стоящее за общей ссылкой, не может быть изменено и что изменяемые ссылки не могут быть псевдонимами. - person Sven Marnach   schedule 07.05.2019let foo: &mut i32 = *z;
я ожидал, что это будет равноlet foo = *z;
, поскольку оно просто делает тип явным. Однако последний вызывает другую ошибку компилятора (невозможно выйти из заимствования). Таким образом, указание типа здесь, похоже, действительно делает следующее:let foo = &mut **z;
. В любом случае: я думаю, что ключевым моментом является то, что средство проверки заимствований не выводит свои правила исключительно из типов, но у него есть дополнительная информация, над которой можно поработать. - person domin   schedule 07.05.2019