Разница между &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 просто не пойман, какие бы то ни было проверки. Возможно, вы могли бы найти или подать проблему, описывающую ситуацию.

Другие вопросы по тегам