借用Checker错误将通用参数添加到struct之后



我有可行的代码,但是它在更改后停止使用借用检查器错误编译。我不明白更改会如何影响借用支票。

工作代码和非工作代码的常见部分:

/// 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实际上为每个T实现Producer,因为它使得无法直接在其上直接调用make。我想拥有针对特定TMyProducer的类型(例如,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:更改之前的工作版本

Rust Playground:变化后破碎版本

i从代码中减少了噪声,因此以下是 broken broken 案例的较短版本,它证明了相同的问题:(在操场上测试(

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的实例的寿命与其make方法中的可变借入相同。因为producer实例不会超出范围(如果它将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方法一样长,因此在调用后,main的范围中不再有生命的可变借入producer,因此您可以拥有另一个。

<</p> <</p>

最新更新