特征对象的 &mut 和 ref mut 之间的区别

Difference between &mut and ref mut for trait objects

首先,我不是在问 &mutref mut 本身有什么区别。

我问是因为我认为:

let ref mut a = MyStruct

相同
let a = &mut MyStruct

考虑return从函数中获取特征对象。您可以 return 一个 Box<Trait> 或一个 &Trait。如果你想对其方法进行可变访问,是否可以 return &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
}

我的理论

此示例将开箱即用地使用不可变引用(无需将其分配给变量,只需 return &English),但不适用于可变引用。我认为这是由于规则,即只能有一个可变引用或任意多个不可变引用。

在不可变引用的情况下,您正在创建一个对象并将其借用为 return 表达式;它的引用不会因为被借用而消失。

在可变引用的情况下,如果您尝试创建一个对象并将其作为 return 表达式可变地借用,您将有两个可变引用(创建的对象及其可变引用)。由于您不能对同一个对象有两个可变引用,因此它不会执行第二个,因此该变量不会存在足够长的时间。我认为当您编写 let mut ref b = English 和 return b 时,您是在 移动 可变引用,因为它被模式捕获。

以上所有内容都不足以向我自己解释它为何有效,但我没有基本原理来证明它。

为什么会这样?

我也cross-posted this question to Reddit.

This is a bug。我下面的原始分析完全忽略了它返回 mutable 引用这一事实。有关提升的位仅在 不可变 值的上下文中才有意义。


由于 rules governing temporaries(强调我的)的细微差别,这是允许的:

When using an rvalue in most lvalue contexts, a temporary unnamed lvalue is created and used instead, if not promoted to 'static.

引用继续:

Promotion of an rvalue expression to a 'static slot occurs when the expression could be written in a constant, borrowed, and dereferencing that borrow where the expression was the originally written, without changing the runtime behavior. That is, the promoted expression can be evaluated at compile-time and the resulting value does not contain interior mutability or destructors (these properties are determined based on the value where possible, e.g. &None always has the type &'static Option<_>, as it contains nothing disallowed).

您的第三个案例可以重写为 "prove" 正在发生 '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只是没有被存在的任何支票抓住。您可能会查找或提交描述情况的问题。