传递给函数的不可变引用后面是可变引用,如何返回可变引用?

5
通过不可变引用传递给函数的可变引用,如何处理?
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(); 
}

会给出以下错误:

error[E0389]: cannot borrow data mutably in a `&` reference
 --> src/main.rs:9:14
  |
8 |         fn func(&'a self) -> &'b mut Foo {
  |                 -------- use `&'a mut self` here to make mutable
9 |              self.f
  |              ^^^^^^ assignment into an immutable reference

我(有点)理解编译器在这里试图防止什么。但是我对错误信息“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
}

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

2
如果允许这样做,您可以使用两个共享引用来创建两个可变引用。 - starblue
1个回答

7

您不能从不可变引用创建可变引用。这意味着您需要将&self更改为&mut self

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

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

let mut bar = Bar { f: &mut foo };
bar.func(); 

“self”(或“self.x”内部)究竟被分配了什么? 错误信息可能有点偏离。您的代码中没有分配,但您正在返回可变引用。可变引用可以让您做的唯一额外的事情就是分配“self.f”或“self.f.i”。这个错误消息肯定可以改进,但它确实包含提示,使“&'a self”可变以解决问题。现在,回答您最初的问题:当将作为参数传递给函数的不可变引用后面的可变引用返回时,如何处理?Rust提供了多种容器类型来进行内部可变性,例如“Cell”和“RefCell”。这些类型将确保正确性的责任从编译器中移除并使其成为运行时检查。一种将“RefCell”应用于您的代码的方法可能是像这样:
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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接