9

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

我问是因为我想:

let ref mut a = MyStruct

是相同的

let a = &mut MyStruct

考虑从函数返回一个特征对象。您可以返回 aBox<Trait>或 a &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
}

我的理论

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

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

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

以上所有内容都是向自己解释它为什么有效的糟糕尝试,但我没有基础知识来证明它。

为什么会这样?

我也将这个问题交叉发布到 Reddit

4

1 回答 1

3

这是一个错误我在下面的原始分析完全忽略了它返回可变引用的事实。关于提升的部分仅在不可变值的上下文中才有意义。


这是允许的,因为管理临时人员的规则有细微差别(强调我的):

在大多数左值上下文中使用右值时,会创建一个临时的未命名左值并改为使用,如果未提升为'static.

参考继续:

'static当表达式可以写入常量、借用和取消引用最初写入表达式的借用位置时,就会将右值表达式提升到槽,而不会更改运行时行为。也就是说,提升的表达式可以在编译时进行评估,并且结果值不包含内部可变性或析构函数(这些属性是根据可能的值确定的,例如&None始终具有 type &'static Option<_>,因为它不包含任何不允许的内容)。

您的第三种情况可以重写为“证明”'static促销正在发生:

fn make_hello_3<'a>() -> &'a mut Hello {
    let ref mut b = English;
    let c: &'static mut Hello = b;
    c
}

As for why ref mut allows this and &mut doesn't, my best guess is that the 'static promotion is on a best-effort basis and &mut just isn't caught by whatever checks are present. You could probably look for or file an issue describing the situation.

于 2017-12-06T18:46:22.510 回答