Can you control structure borrowing and field borrowing?

I am working on a program containing a structure on the following lines:

struct App {
    data: Vec<u8>,
    overlay: Vec<(usize, Vec<u8>)>,

    sink: Sink,
}

      

In short, the field data

contains several bytes, and overlay

is a sequence of sequences of bytes to be inserted at specific indices. The type Sink

is unimportant, except that it has a function like:

impl Sink {
    fn process<'a>(&mut self, input: Vec<&'a [u8]>) {
        ...
    }
}

      

I used an iterator to combine information from data

and overlay

to use Sink

.

struct MergeIter<'a, 'b> {
    data: &'a Vec<u8>,
    overlay: &'b Vec<(usize, Vec<u8>)>,
    ... // iterator state etc.
}

impl<'a, 'b> Iterator for MergeIter<'a, 'b> {
    type Item = &'a [u8];
    ...
}

      

This is a bit of a lie, I think, because the lifetime of each & [u8] returned by the iterator does not always match the lifetime of the original data

. The data inserted from overlay

has a different lifetime, but I don't see how I can comment on it more accurately. Anyway, the loan validator doesn't seem to mind - the following approach works:

fn merge<'a, 'b>(data: &'a Vec<u8>, overlay: &'b Vec<(usize, Vec<u8>)>, start: usize) -> Vec<&'a [u8]> {
    MergeIter::new(data, overlay, start).collect()
}

impl App {
    ...
    fn process(&mut self) {
        let merged = merge(&self.data, &self.overlay, 0);
        ... // inspect contents of 'merged'
        self.sink.process(merged);
    }
}

      

I end up using this feature merge

all over the place, but always against the same data / overlay. So I suppose I'll add a function App::merge

for convenience, and here's where the problem starts:

impl App {
    ...
    fn merge<'a>(&'a self, start: usize) -> Vec<&'a [u8]> {
        MergeIter::new(&self.data, &self.overlay, start).collect()
    }

    fn process(&mut self) {
        let merged = self.merge(0);
        ... // inspect contents of 'merged'
        self.sink.process(merged);
    }
}

      

App::process

now the borrower cannot pass verification - he refuses to allow volatile borrowing self.sink, and he himself is busy.

I have been struggling with this for some time, and if I understood correctly, the problem is not with process

, but with this signature:

    fn merge<'a>(&'a self, start: usize) -> Vec<&'a [u8]> {

      

Here I essentially said that checking for borrowing, that the references returned in the vector are equivalent to borrowing self

.

Even though I feel like I understand the problem now, I still feel like my hands are tied. Leaving annotations for life doesn't help (because the compiler does the equivalent?), And with only two linked links, there is no clue to tell rust that the exit link has a lifetime attachment to something else.

I also tried this:

    fn merge<'a, 'b>(&'b self, start: usize) -> Vec<&'a [u8]> {
        let data : &'a Vec<u8> = &self.data;
        MergeIter::new(&self.data, &self.overlay, start).collect()
    }

      

but the compiler complains about the operator let

("unable to infer the appropriate lifespan due to conflicting requirements" - I also think it pisses me off that the compiler doesn't explain the mentioned requirements).

Can this be achieved? Rust reference - kind of light on lifetime annotations and related syntax ...

rustc 1.0.0-nightly (706be5ba1 2015-02-05 23:14:28 +0000)

+3


source to share


2 answers


Yes, you guessed it right - the error occurs because when you have an merge

accept method &self

, the compiler cannot know on its calling site that it only uses some fields - the signature merge

only tells this that the returned data is somehow inferred from self

, but it doesn't tell you how - and so the compiler accepts the "worst" case and doesn't allow you to access other fields self

.

I'm afraid there is currently no way to fix this, and I'm not sure if there will ever be one. However, you can use macros to shorten the merge

invocations:



macro_rules! merge {
    ($this:ident, $start:expr) => {
        MergeIter::new(&$this.data, &$this.overlay, $start).collect()
    }
}

// ...

fn process(&mut self) {
    let merged = merge!(self, 0);
    ... // inspect contents of 'merged'
    self.sink.process(merged);
}

      

+2


source


While the method merge

accepts &self

, you cannot do what you want: it borrows all of its arguments, and this cannot be changed.

The solution is to change it so that it doesn't accept self

, but instead takes in the individual fields you want to borrow:



impl App {
    ...
    fn merge(data: &Vec<u8>, overlay: &Vec<(usize, Vec<u8>)>, start: usize) -> Vec<&[u8]> {
        MergeIter::new(data, overlay, start).collect()
    }

    fn process(&mut self) {
        let merged = Self::merge(&self.data, &self.overlay, 0);
        ... // inspect contents of 'merged'
        self.sink.process(merged);
    }
}

      

+5


source







All Articles