向结构添加通用参数后出现借入检查器错误

时间:2019-05-09 11:28:21

标签: rust borrow-checker

我有可用的代码,但更改后它停止编译并出现借用检查器错误。我不知道该更改如何影响借入支票。

有效代码和无效代码的共同部分:

/// Some struct that has references inside
#[derive(Debug)]
struct MyValue<'a> {
    number: &'a u32,
}

/// There are many structs similar to `MyValue` and there is a
/// trait common to them all that can create them. In this
/// example I use the `From` trait.
impl<'a> From<&'a u32> for MyValue<'a> {
    fn from(value: &'a u32) -> Self {
        MyValue { number: value }
    }
}

/// `Producer` makes objects that hold references into it. So
/// the produced object must be first dropped before any new
/// one can be made.
trait Producer<'a, T: 'a> {
    fn make(&'a mut self) -> T;
}

这是工作代码:

struct MyProducer {
    number: u32,
}

impl MyProducer {
    fn new() -> Self {
        Self { number: 0 }
    }
}

impl<'a, T: 'a + From<&'a u32>> Producer<'a, T> for MyProducer {
    fn make(&'a mut self) -> T {
        self.number += 1;
        T::from(&self.number)
    }
}

fn main() {
    let mut producer = MyProducer::new();

    println!(
        "made this: {:?}",
        <MyProducer as Producer<MyValue>>::make(&mut producer)
    );
    println!(
        "made this: {:?}",
        <MyProducer as Producer<MyValue>>::make(&mut producer)
    );
}

这将编译并打印预期的输出:

made this: MyValue { number: 1 }
made this: MyValue { number: 2 }

我不喜欢MyProducer实际上为每个Producer实现T,因为它不可能直接在其上调用make。我想为特定的MyProducer(例如T)使用MyValue类型。

要实现这一点,我想向MyProducer添加一个通用参数。因为MyProducer并未真正使用T,所以我使用PhantomData来防止编译器抱怨。

以下是更改后的代码:

use std::marker::PhantomData;

struct MyProducer<'a, T: 'a + From<&'a u32>> {
    number: u32,
    _phantom: PhantomData<&'a T>,
}

impl<'a, T: 'a + From<&'a u32>> MyProducer<'a, T> {
    fn new() -> Self {
        Self {
            number: 0,
            _phantom: PhantomData::default(),
        }
    }
}

impl<'a, T: From<&'a u32>> Producer<'a, T> for MyProducer<'a, T> {
    fn make(&'a mut self) -> T {
        self.number += 1;
        T::from(&self.number)
    }
}

fn main() {
    let mut producer = MyProducer::<MyValue>::new();

    println!("made this: {:?}", producer.make());
    println!("made this: {:?}", producer.make());
}

main函数现在看起来完全像我想要的样子。但是代码无法编译。这是错误:

error[E0499]: cannot borrow `producer` as mutable more than once at a time
  --> src/main.rs:50:33
   |
49 |     println!("made this: {:?}", producer.make());
   |                                 -------- first mutable borrow occurs here
50 |     println!("made this: {:?}", producer.make());
   |                                 ^^^^^^^^
   |                                 |
   |                                 second mutable borrow occurs here
   |                                 first borrow later used here

我不明白为什么它不再起作用。在制作下一个对象之前,仍将其丢弃。

如果我只调用一次make函数,它将编译并运行。

我正在使用2018版,因此NLL已启用。

Rust Playground: working version before change

Rust Playground: broken version after change

1 个答案:

答案 0 :(得分:0)

我从代码中减少了噪声,因此以下是 broken 案例的更短版本,它演示了相同的问题:(test in the playground)< / p>

use std::marker::PhantomData;

#[derive(Debug)]
struct MyValue<'a>(&'a u32);

impl<'a> From<&'a u32> for MyValue<'a> {
    fn from(value: &'a u32) -> Self {
        MyValue(value)
    }
}

struct MyProducer<'a, T>(u32, PhantomData<&'a T>);

impl<'a, T> MyProducer<'a, T>
where
    T: From<&'a u32>,
{
    fn new() -> Self {
        Self(0, PhantomData)
    }

    fn make(&'a mut self) -> T {
        self.0 += 1;
        T::from(&self.0)
    }
}

fn main() {
    let mut producer = MyProducer::<MyValue>::new();
    println!("made this: {:?}", producer.make());
    println!("made this: {:?}", producer.make());
}

这里的主要问题是,可变借项的生存期是MyProducer的生存期,也就是说,名为producer的实例的生存期与其在{{1 }} 方法。由于make实例不会超出范围(如果这样的话producer将无法保存对存储在其中的值的引用),因此可变借项的有效期一直到{ {1}}的范围。借用的第一个规则是,在任何时候范围内只能有一个给定值的可变变量借用,因此会产生编译器错误。

如果您在这里查看我的解决方案,该解决方案实际上正在工作,并且按照我认为您希望的那样进行:(test in the playground):

MyValue

然后您会看到可变借项的有效期限仅与main方法一样长,因此在调用之后,在#[derive(Debug)] struct MyValue<'a>(&'a u32); impl<'a> From<&'a u32> for MyValue<'a> { fn from(value: &'a u32) -> Self { MyValue(value) } } struct MyProducer(u32); impl MyProducer { fn new() -> Self { Self(0) } fn make<'a, T>(&'a mut self) -> T where T: From<&'a u32>, { self.0 += 1; T::from(&self.0) } } fn main() { let mut producer = MyProducer::new(); println!("made this: {:?}", producer.make::<MyValue>()); println!("made this: {:?}", producer.make::<MyValue>()); } 范围内不再有make的有效可变借项,这样您可以再拥有一个。