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?