4

When using a Future in Rust, it is common to pass ownership of an object (e.g. connection, processed data, etc) between chained processing steps implemented with lambdas. I understand the concept and did this a lot already without problems.

I'm trying to do the same but this time a partial result is a reference type. I cannot convince the Rust borrow checker to accept the following (overly simplified) code:

extern crate futures;
use futures::prelude::*;

// Parsed data with attribute values that might be not owned, only referenced
trait Data<'a> {
    fn attribute<'s, 'n>(&'s self, name: &'n str) -> &'a str;
}

fn async_load_blob() -> Box<Future<Item = Vec<u8>, Error = ()>> {
    Box::new(futures::future::err(())) // Dummy impl to compile
}

fn parse<'a>(_blob: &'a [u8]) -> Result<Box<Data<'a> + 'a>, ()> {
    Err(()) // Dummy impl just to compile fine
}

fn resolve_attribute<'a, 'n>(
    name: &'n str,
) -> Box<Future<Item = (Vec<u8>, &'a str), Error = ()> + 'a> {
    let owned_name = name.to_owned(); // move attribute name into lambda
    let fut = async_load_blob().and_then(move |blob| {
        // COMPILE ERROR: how to convince borrow checker that the
        // owned data is properly moved out together with the reference?
        let data_res = parse(blob.as_slice());
        match data_res {
            Ok(data) => {
                let attr = data.attribute(owned_name.as_str());
                futures::future::ok((blob, attr))
            }
            Err(e) => futures::future::err(e),
        }
    });
    Box::new(fut)
}

The problematic part is the tuple returned in the successful branch. If I try to return (thus move out) the owned data from the scope, the borrow checker cannot seem to understand the correlation between them and reports an error.

I also tried to use Rc and other tricks already and failed every time. Is this possible to express and fix in Rust, or is the whole concept fundamentally flawed and should be implemented differently, e.g. by returning attributes as owned values, thus copying instead of referencing?

0

1 Answer 1

1

What you have there is essentially an interior reference (the tuple contains a reference into its other element), which is very tricky in Rust. The borrow checker cannot distinguish between a reference to the object itself (which moves and thus would invalidate the reference) and things the object owns (e.g. string data on the heap, which would be stable).

The rental crate tries to solve this problem. You can use it to replace the tuple with a custom struct which is capable of referencing the heap data it owns.

2
  • Thanks for the super fast reply! I also tried returning custom structs binding the two values together, but also failed. Does the rental crate use unsafe code to achieve this? I'm not very familiar with that yet. Commented Jan 5, 2018 at 16:49
  • @ZólyomiIstván yes, it uses unsafe code.
    – Shepmaster
    Commented Jan 5, 2018 at 17:09

Not the answer you're looking for? Browse other questions tagged or ask your own question.