返回传递给函数的不可变引用后面的可变引用

sou*_*ics 4 reference mutable immutability rust

如何返回位于不可变引用后面的可变引用,作为参数传递给函数,如何处理?

struct Foo { i: i32 }

struct Bar<'b> {
    f: &'b mut Foo
}

impl<'a: 'b, 'b> Bar<'b> {
    fn func(&'a self) -> &'b mut Foo {
         self.f
    }
}

fn main() {
    let mut foo = Foo { i: 1 };

    let bar = Bar { f: &mut foo };
    bar.func(); 
}
Run Code Online (Sandbox Code Playgroud)

给出以下错误:

struct Foo { i: i32 }

struct Bar<'b> {
    f: &'b mut Foo
}

impl<'a: 'b, 'b> Bar<'b> {
    fn func(&'a self) -> &'b mut Foo {
         self.f
    }
}

fn main() {
    let mut foo = Foo { i: 1 };

    let bar = Bar { f: &mut foo };
    bar.func(); 
}
Run Code Online (Sandbox Code Playgroud)

我(有点)理解编译器在这里试图阻止什么。但我对错误消息感到困惑assignment into an immutable reference。究竟是什么被分配到self(或里面self.f?)?

我写了下面的代码来模拟这个问题,得到了同样的错误信息,与上面的不同,我能够理解。这是代码:

fn main() {
    let mut foo = Foo { i: 1 };

    let bar = Bar { f: &mut foo };
    let pbar = &bar;

    pbar.f.i = 2; // assignment into an immutable reference
}
Run Code Online (Sandbox Code Playgroud)

在第一个示例中,它是否试图将可变引用f移出self(因为&mut它不是Copy类型),将其视为不可变引用内的突变self,因此出现错误消息assignment into an immutable reference

Pet*_*all 7

你不能从一个不可变的引用创建一个可变的引用。这意味着您需要更改&self&mut self

impl<'a: 'b, 'b> Bar<'b> {
    fn func(&'a mut self) -> &'b mut Foo {
         self.f
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您的变量需要是可变的,以便您可以为该方法获取对它的可变引用:

let mut bar = Bar { f: &mut foo };
bar.func(); 
Run Code Online (Sandbox Code Playgroud)

究竟是什么被分配到 self (或里面self.x?)?

错误消息可能有点偏离。您的代码中没有赋值,但您返回的是可变引用。可变引用可以让您在这里做的唯一额外的事情是分配self.fself.f.i

肯定可以改进此错误消息,但它确实包含一个提示,使&'a self可变的修复问题。

现在,你原来的问题:

如何返回位于不可变引用后面的可变引用,作为参数传递给函数,如何处理?

Rust 为内部可变性提供了多种容器类型,例如CellRefCell。这些类型负责确保编译器的正确性,并使其成为运行时检查。将 aRefCell应用于您的代码的一种方法可能是这样的:

use std::cell::RefCell;
use std::ops::DerefMut;

struct Foo { i: i32 }

struct Bar<'b> {
    // store the data in a RefCell for interior mutability
    f: &'b RefCell<Foo>
}

impl<'a: 'b, 'b> Bar<'b> {
    // Return a RefMut smart pointer instead of mutable ref, but hide the implementation,
    // just exposing it as something that can be mutably dereferenced as a Foo
    fn func(&'a self) -> impl DerefMut<Target = Foo> + 'b {
         self.f.borrow_mut()
    }
}

fn main() {
    let foo = RefCell::new(Foo { i: 1 });
    let bar = Bar { f: &foo };

    let mut f = bar.func(); 
    f.i = 3;
}
Run Code Online (Sandbox Code Playgroud)