Разница между &mut и ref mut для объектов черты
Прежде всего, я не спрашиваю, в чем разница между &mut
а также ref mut
как таковой.
Я спрашиваю, потому что я думал:
let ref mut a = MyStruct
такой же как
let a = &mut MyStruct
Подумайте о возвращении объекта-черты из функции. Вы можете вернуть Box<Trait>
или &Trait
, Если вы хотите иметь изменяемый доступ к его методам, можно ли вернуть &mut Trait
?
Учитывая этот пример:
trait Hello {
fn hello(&mut self);
}
struct English;
struct Spanish;
impl Hello for English {
fn hello(&mut self) {
println!("Hello!");
}
}
impl Hello for Spanish {
fn hello(&mut self) {
println!("Hola!");
}
}
Метод получает изменяемую ссылку для демонстрационных целей.
Это не скомпилируется:
fn make_hello<'a>() -> &'a mut Hello {
&mut English
}
ни это
fn make_hello<'a>() -> &'a mut Hello {
let b = &mut English;
b
}
Но это скомпилирует и сработает:
fn make_hello<'a>() -> &'a mut Hello {
let ref mut b = English;
b
}
Моя теория
Этот пример будет работать из коробки с неизменяемыми ссылками (необязательно присваивать его переменной, просто вернуть &English
) но не с изменяемыми ссылками. Я думаю, что это связано с правилом, согласно которому может быть только одна изменяемая ссылка или столько неизменных, сколько вы хотите.
В случае неизменяемых ссылок вы создаете объект и заимствуете его как выражение возврата; его ссылка не умрет, потому что он заимствован.
В случае изменяемых ссылок, если вы пытаетесь создать объект и заимствовать его в качестве возвращаемого выражения, у вас есть две изменяемые ссылки (созданный объект и его изменяемая ссылка). Поскольку у вас не может быть двух изменяемых ссылок на один и тот же объект, он не будет выполнять второй, поэтому переменная не проживет достаточно долго. Я думаю, что когда ты пишешь let mut ref b = English
и вернуться b
Вы перемещаете изменяемую ссылку, потому что она была захвачена шаблоном.
Все вышеперечисленное - плохая попытка объяснить себе, почему это работает, но у меня нет оснований, чтобы это доказать.
Почему это происходит?
Я также отправил этот вопрос в Reddit.
1 ответ
Это ошибка. Мой оригинальный анализ ниже полностью игнорировал тот факт, что он возвращал изменчивую ссылку. Биты о продвижении имеют смысл только в контексте неизменных ценностей.
Это допустимо из-за нюансов правил, регулирующих временные отношения (выделено мной):
При использовании rvalue в большинстве контекстов lvalue создается временное безымянное lvalue и используется вместо него, если не повышен до
'static
,
Ссылка продолжается:
Продвижение rvalue выражения к
'static
временной интервал возникает, когда выражение может быть записано в константе, заимствованной и разыменованной, которая заимствует там, где выражение было изначально написано, без изменения поведения во время выполнения. Таким образом, продвигаемое выражение может быть оценено во время компиляции, и результирующее значение не содержит внутреннюю изменчивость или деструкторы (эти свойства определяются на основе значения, где это возможно, например,&None
всегда имеет тип&'static Option<_>
, так как в нем ничего не запрещено).
Ваш третий случай может быть переписан так, чтобы "доказать", что 'static
продвижение происходит:
fn make_hello_3<'a>() -> &'a mut Hello {
let ref mut b = English;
let c: &'static mut Hello = b;
c
}
Почему ref mut
позволяет это и &mut
нет, я думаю, что 'static
продвижение на основе максимальных усилий и &mut
просто не пойман, какие бы то ни было проверки. Возможно, вы могли бы найти или подать проблему, описывающую ситуацию.