Borrow checker error after adding generic paramete

2019-07-18 09:41发布

问题:

I have code that works, but it stops compiling with a borrow checker error after a change. I don't understand how the change could affect borrow checking.

Common part to both working and non-working code:

/// 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;
}

Here is the working code:

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)
    );
}

This compiles and prints the expected output:

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

I don't like that MyProducer actually implements Producer for every T as it makes it impossible to call make directly on it. I would like to have a type that is a MyProducer for a specific T (for example for MyValue).

To achieve this, I want to add a generic parameter to MyProducer. Because the MyProducer does not really use the T, I use PhantomData to prevent the compiler from complaining.

Here is the code after changes:

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());
}

The main function now looks exactly as I would like it to look like. But the code does not compile. This is the error:

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

I don't understand why it no longer works. The produced object is still dropped before the next one is made.

If I call the make function just once, it compiles and works.

I am using edition 2018, so NLL is active.

Rust Playground: working version before change

Rust Playground: broken version after change

回答1:

I reduced the noise from the code so the following is an even shorter version of the broken case which demonstrates the same problem: (test in the playground)

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());
}

The main problem here is that the mutable borrow's lifetime is the lifetime of MyProducer, that is, the lifetime of the instance called producer is the same as the mutable borrow taken in its make method. Because the producer instance does not go out of scope (if it would then MyValue wouldn't be able to hold a reference to a value stored in it) so the mutable borrow lives until the end of main's scope. The first rule of borrowing is that there can only be a single mutable borrow of a given value in a scope at any time, hence the compiler error.

If you are looking at my solution here, which is actually working and does what I think you wanted it to: (test in the playground):

#[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>());
}

then you can see that the mutable borrow only lives as long as the make method, therefore after the invocation there's no more living mutable borrow to producer in main's scope, thus you can have another one.