无法作为不可变的借用,因为它也被作为可变的借用。

8
我正在使用一个库中的结构体FooBar,但在客户端代码中出现了编译错误。我将代码简化为以下内容:
use std::marker::PhantomData;

struct Foo {
    some_str: &'static str,
}

struct Bar<'a> {
    some_str: &'static str,
    marker: PhantomData<&'a Foo>,
}

impl Foo {
    fn read_data(&self) {
        // add code here
    }
    fn create_bar<'a>(&'a mut self) -> Bar<'a> {
        Bar {
            some_str: "test2",
            marker: PhantomData,
        }
    }
}

fn process(_arr: &mut [Bar]) {}

fn main() {
    let mut foo = Foo { some_str: "test" };
    let mut array: [Bar; 1] = [foo.create_bar()];
    process(&mut array);
    foo.read_data();
}

(Playground)

输出:

error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
  --> src/main.rs:30:5
   |
28 |     let mut array: [Bar; 1] = [foo.create_bar()];
   |                                --- mutable borrow occurs here
29 |     process(&mut array);
30 |     foo.read_data();
   |     ^^^ immutable borrow occurs here
31 | }
   | - mutable borrow ends here

控制台输出中的错误非常清晰,但我无法解决问题。
2个回答

5
您可以通过使用花括号({ ... })将array变量放置在新的作用域中来限制其寿命:
fn main() {
    let mut foo = Foo { some_str: "test" };
    {
        let mut array: [Bar; 1] = [foo.create_bar()];
        process(&mut array);
    }
    foo.read_data();
}

是否可以明确指定变量'foo'的生命周期比没有方括号的数组变量更长? - dmgcodevil
不,我认为那是不可能的。 - aSpex
@aSpex的create_bar函数需要一个&'a mut self:这是在以可变方式借用foo。尽管在这个例子中实际上没有使用到self,但是'a寿命将从分配到数组到数组超出范围的时刻。通过括号限制了数组的作用域并结束了可变借用。正在进行工作以添加“非词法生存期”,使得Rust能够聪明地自行判断'a在此情况下实际上不需要一直到函数结束,因为arrayfoo.read_data之后未被使用 - Paolo Falabella
@aSpex,你所做的大致相当于这个。我同意Rust在这里有点太过严格了,而你实际上并没有做出不安全的行为。只是现在Rust还不知道如何以那种粒度查看生命周期。 - Paolo Falabella
1
@aSpex 生命周期的限制 在 Rustonomicon 中有更详细的介绍。 - Paolo Falabella
显示剩余2条评论

4

一旦默认启用非词法生命周期,您的原始代码将按原样工作:

#![feature(nll)]

use std::marker::PhantomData;

struct Foo {
    some_str: &'static str,
}

struct Bar<'a> {
    some_str: &'static str,
    marker: PhantomData<&'a Foo>,
}

impl Foo {
    fn read_data(&self) {
        // add code here
    }
    fn create_bar<'a>(&'a mut self) -> Bar<'a> {
        Bar {
            some_str: "test2",
            marker: PhantomData,
        }
    }
}

fn process(_arr: &mut [Bar]) {}

fn main() {
    let mut foo = Foo { some_str: "test" };
    let mut array: [Bar; 1] = [foo.create_bar()];
    process(&mut array);
    foo.read_data();
}

NLL(非诉讼借用检查器)使得借用检查器更加先进和精确;现在它可以理解你在调用process后没有再使用array,因此在新的方式中使用foo是安全的。


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