I'm trying to use a struct created in main() and pass it on to a function that returns a boxed Future. However, I run into lifetime and borrowing issues and can't seem to resolve this cleanly.
Here is my struct and functions:
extern crate futures; // 0.1.21
extern crate tokio_core; // 0.1.17
use futures::{future::ok, Future};
pub struct SomeStruct {
some_val: u32,
}
impl SomeStruct {
pub fn do_something(&self, value: u32) -> u32 {
// Do some work
return self.some_val + value;
}
}
fn main() {
let core = tokio_core::reactor::Core::new().unwrap();
let my_struct = SomeStruct { some_val: 10 };
let future = get_future(&my_struct);
core.run(future);
let future2 = get_future(&my_struct);
core.run(future2);
}
fn get_future(some_struct: &SomeStruct) -> Box<Future<Item = u32, Error = ()>> {
let fut = ok(20).and_then(|val| {
let result = some_struct.do_something(val);
ok(result)
});
Box::new(fut)
}
On compiling, the following error occurs:
error[E0621]: explicit lifetime required in the type of `some_struct`
--> src/main.rs:33:5
|
28 | fn get_future(some_struct: &SomeStruct) -> Box<Future<Item = u32, Error = ()>> {
| ----------- consider changing the type of `some_struct` to `&'static SomeStruct`
...
33 | Box::new(fut)
| ^^^^^^^^^^^^^ lifetime `'static` required
I suppose the error occurs because SomeStruct is used in the Future and might be used outside of main()s scope, hence the compiler asks me to change the lifetime to 'static. Here is what I tried so far (unsuccessfully):
Changing the lifetime to 'static as suggested by the compiler, which creates borrowing issues in main().
Moving val by adding ok(20).and_then(move |val| { as suggested by the compiler, which creates issues in the second invocation of get_future().
Use the lazy_static crate to explicitly initialize SomeStruct as static (as suggested here), however I run into macro errors when trying that.
The whole example can be found here. I have omitted some details to create an minimal example. The issues occur using tokio-core and futures = "0.1". Migrating to version "0.2" is not an option unfortunately, due to a dependency of another library.
Returning a boxed trait object has a 'static bound by default. Do as the compiler suggests and provide an explicit lifetime, but not 'static:
fn get_future<'a>(some_struct: &'a SomeStruct) -> Box<dyn Future<Item = u32, Error = ()> + 'a> {
let fut = future::ok(20).and_then(move |val| {
let result = some_struct.do_something(val);
future::ok(result)
});
Box::new(fut)
}
You also have to use move to transfer ownership of some_struct to the closure and change core to be mutable. You should also handle potential errors resulting from core.run.
For the example provided, you could also return impl Future:
fn get_future<'a>(some_struct: &'a SomeStruct) -> impl Future<Item = u32, Error = ()> +'a {
future::ok(20).and_then(move |val| {
let result = some_struct.do_something(val);
future::ok(result)
})
}
See also:
How do I return a boxed closure from a method that has a reference to the struct?
Why is adding a lifetime to a trait with the plus operator (Iterator<Item = &Foo> + 'a) needed?
What is the correct way to return an Iterator (or any other trait)?
Related
Please consider the following example (playground):
struct Animal<'a> {
format: &'a dyn Fn() -> (),
}
impl <'a>Animal<'a> {
pub fn set_formatter(&mut self, _fmt: &'a dyn Fn() -> ()) -> () {} // Getting rid of 'a here satisfies the compiler
pub fn bark(&self) {}
}
fn main() {
let mut dog: Animal = Animal { format: &|| {()} };
let x = 0;
dog.set_formatter(&|| {
println!("{}", x); // Commenting this out gets rid of the error. Why?
});
dog.bark(); // Commenting this out gets rid of the error. Why?
}
This gives the following compilation error:
Compiling playground v0.0.1 (/playground)
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:13:24
|
13 | dog.set_formatter(&|| {
| ________________________^
14 | | println!("{}", x); // Commenting this out gets rid of the error. Why?
15 | | });
| | ^ - temporary value is freed at the end of this statement
| |_____|
| creates a temporary which is freed while still in use
16 | dog.bark(); // Commenting this out gets rid of the error. Why?
| --- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error: aborting due to previous error
For more information about this error, try `rustc --explain E0716`.
error: could not compile `playground`
To learn more, run the command again with --verbose.
This kind of makes sense as the closure that I'm passing to dog.set_formatter(...) is indeed a temporary which (I guess) is freed when execution proceeds to dog.bark();.
I know that getting rid of the explicit lifetime annotation in the implementation of set_formatter seems to satisfy the compiler (note the missing 'a before dyn):
pub fn set_formatter(&mut self, _fmt: & dyn Fn() -> ()) -> () {}
However, I don't understand the following:
Why does the problem go away when I comment out println!("{}", x); inside the closure? I'm still passing a temporary which I expect the compiler to complain about, but it doesn't.
Why does the problem go away when I comment out dog.bark(); at the end? Again, I'm still passing a temporary closure which is freed but now the compiler is happy. Why?
The first thing to understand is that &|| () has a 'static lifetime:
fn main() {
let closure: &'static dyn Fn() = &|| (); // compiles
}
Another thing worth mentioning is that a closure's lifetime cannot exceed the lifetime of any of the variables it captures from its environment, which is why if we try to pass a non-static variable to our static closure it would fail to compile:
fn main() {
let x = 0; // non-static temporary variable
let closure: &'static dyn Fn() = &|| {
println!("{}", x); // x reference captured by closure
}; // error: trying to capture non-static variable in static closure
}
We'll come back to that. Anyway, so if I have a struct which is generic over references, and I pass it a 'static reference, I'll have a 'static instance of that struct:
struct Dog<'a> {
format: &'a dyn Fn(),
}
fn main() {
let dog: Dog<'static> = Dog { format: &|| () }; // compiles
}
The second thing to understand is once you instantiate a type you cannot change it. This includes any of its generic parameters, including lifetimes. Once you have a Dog<'static> it will always be a Dog<'static>, you cannot convert it into a Dog<'1> for some anonymous lifetime '1 that is shorter than 'static.
This has some strong implications, one of which is that your set_formatter method probably doesn't behave how you think it behaves. Once you have a Dog<'static> you can only pass 'static formatters to set_formatter. The method looks like this:
impl<'a> Dog<'a> {
fn set_formatter(&mut self, _fmt: &'a dyn Fn()) {}
}
But since we know we're working with a Dog<'static> we can substitute out the generic lifetime parameter 'a with 'static to see what we're really working with:
// what the impl would be for Dog<'static>
impl Dog {
fn set_formatter(&mut self, _fmt: &'static dyn Fn()) {}
}
So now that we've gotten all of that context out of the way let's get to your actual questions.
Why does the problem go away when I comment out println!("{}", x); inside the closure? I'm still passing a temporary which I expect the compiler to complain about, but it doesn't.
Why it fails, with comments:
struct Dog<'a> {
format: &'a dyn Fn(),
}
impl<'a> Dog<'a> {
fn set_formatter(&mut self, _fmt: &'a dyn Fn()) {}
}
fn main() {
let mut dog: Dog<'static> = Dog { format: &|| () };
// x is a temporary value on the stack with a non-'static lifetime
let x = 0;
// this closure captures x by reference which ALSO gives it a non-'static lifetime
// and you CANNOT pass a non-'static closure to a Dog<'static>
dog.set_formatter(&|| {
println!("{}", x);
});
}
The reason this error is "fixed" by commenting out the line println!("{}", x); is because it gives the closure a 'static lifetime again since it no longer borrows the non-'static variable x.
Why does the problem go away when I comment out dog.bark(); at the end? Again, I'm still passing a temporary closure which is freed but now the compiler is happy. Why?
This weird edge case only seems to happen when we don't explicitly type-annotate the dog variable with Dog<'static>. When a variable doesn't have an explicit type annotation the compiler attempts to infer its type, but it does so lazily, and tries to be as flexible as possible, giving the benefit of the doubt to the programmer, in order to make the code compile. It really should throw a compile error even without the dog.bark() but it doesn't for whatever mysterious reasons. The point is it's not the dog.bark() line that is making the code fail to compile, it should be failing to compile at the set_formatter line regardless, but for whatever reason the compiler doesn't bother to throw an error until you try to use dog again after that offending line. Even just dropping dog will trigger the error:
struct Dog<'a> {
format: &'a dyn Fn(),
}
impl<'a> Dog<'a> {
fn set_formatter(&mut self, _fmt: &'a dyn Fn()) {}
}
fn main() {
let mut dog = Dog { format: &|| () };
let x = 0;
dog.set_formatter(&|| {
println!("{}", x);
});
drop(dog); // triggers "temp freed" error above
}
And since we've come this far, let's answer your unofficial third question, paraphrased by me:
Why does getting rid of the 'a in the set_formatter method satisfy the compiler?
Because it changes what is effectively this for Dog<'static>:
// what the impl would be for Dog<'static>
impl Dog {
fn set_formatter(&mut self, _fmt: &'static dyn Fn()) {}
}
Into this:
// what the impl would now be for Dog<'static>
impl Dog {
fn set_formatter(&mut self, _fmt: &dyn Fn()) {}
}
So now you can pass non-'static closures to a Dog<'static> although it's pointless since the method doesn't actually do anything, and the compiler will complain again the moment you actually try to set the closure in the Dog<'static> struct.
I have this piece of code using futures v0.1:
impl ArcService for (Box<MiddleWare<Request>>, Box<ArcService>) {
fn call(&self, req: Request, res: Response) -> Box<Future<Item = Response, Error = Error>> {
box self.0.call(req).and_then(move |req| self.1.call(req, res))
}
}
pub trait ArcService: Send + Sync {
fn call(&self, req: Request, res: Response) -> Box<Future<Item = Response, Error = Error>>;
}
pub trait MiddleWare<T>: Sync + Send {
fn call<'a>(&'a self, param: T) -> Box<Future<Item = T, Error = Error> + 'a>;
}
type MiddleWareFuture<'a, I> = Box<Future<Item = I, Error = Error> + 'a>;
impl MiddleWare<Request> for Vec<Box<MiddleWare<Request>>> {
fn call(&self, request: Request) -> MiddleWareFuture<Request> {
self.iter()
.fold(box Ok(request).into_future(), |request, middleware| {
box request.and_then(move |req| middleware.call(req))
})
}
}
pub struct ArcRouter {
routes: HashMap<Method, Box<ArcService>>,
}
// Service implementation
impl hyper::Server::Service for ArcRouter {
type Response = Response;
type Request = Request;
type Error = hyper::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, req: Request) -> Box<Future<Item = Self::Response, Error = Self::Error>> {
if let Some(routeMatch) = self.matchRoute(req.path(), req.method()) {
let mut request: ArcRequest = req.into();
request.paramsMap.insert(routeMatch.params);
let response = routeMatch.handler //handler is ArcService
.call(request, ArcResponse::new())
.map(|res| res.into());
return box response;
}
// TODO: this should be handled by a user defined 404 handler
return box Ok(Response::new().with_status(StatusCode::NotFound)).into_future();
}
}
Note the lifetime parameter on Middleware — it is used to avoid lifetime issues.
This does not compile because Box<Future<Item = Response, Error = Error>> is implicitly 'static and therefore causes lifetime issues. hyper::Server::Service requires a 'static Future
Here is an example that aptly describes my problem:
extern crate futures; // v0.1 (old)
use futures::{future, Future};
struct Example {
age: i32,
}
// trait is defined in an external crate. You can't change it's definition
trait MakeFuture {
fn make_a_future(&self) -> Box<Future<Item = i32, Error = ()>>;
}
impl MakeFuture for Example {
fn make_a_future(&self) -> Box<Future<Item = i32, Error = ()>> {
let f = future::ok(self).map(|ex| ex.age + 1);
Box::new(f)
}
}
playground link
which gives the lifetime error:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:16:28
|
16 | let f = future::ok(self).map(|ex| ex.age + 1);
| ^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 15:5...
--> src/main.rs:15:5
|
15 | / fn make_a_future(&self) -> Box<Future<Item = i32, Error = ()>> {
16 | | let f = future::ok(self).map(|ex| ex.age + 1);
17 | | Box::new(f)
18 | | }
| |_____^
note: ...so that expression is assignable (expected &Example, found &Example)
--> src/main.rs:16:28
|
16 | let f = future::ok(self).map(|ex| ex.age + 1);
| ^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that expression is assignable (expected std::boxed::Box<futures::Future<Item=i32, Error=()> + 'static>, found std::boxed::Box<futures::Future<Item=i32, Error=()>>)
--> src/main.rs:17:9
|
17 | Box::new(f)
| ^^^^^^^^^^^
Is there a way to get around this? I'm building with hyper::Service and using Rust v1.25.0 (nightly)
How to return a future combinator with &self
You return a future that refers to self like this:
use futures::future::{self, FutureResult}; // 0.1.28
struct Example {
age: i32,
}
impl Example {
fn make_a_future(&self) -> FutureResult<&Example, ()> {
future::ok(self)
}
}
As discussed in the Tokio documentation on returning futures, the easiest stable solution to returning a complicated future is a impl Trait. Note that we assign an explicit lifetime to self and use that in the returned value (via + 'a):
use futures::{future, Future}; // 0.1.28
struct Example {
age: i32,
}
impl Example {
fn make_a_future<'a>(&'a self) -> impl Future<Item = i32, Error = ()> + 'a {
future::ok(self).map(|ex| ex.age + 1)
}
}
Your real question is "how can I lie to the compiler and attempt to introduce memory unsafety into my program?"
Box<SomeTrait + 'static> (or Box<SomeTrait> by itself) means that the trait object must not contain any references that do not last for the entire program. By definition, your Example struct has a shorter lifetime than that.
This has nothing to do with futures. This is a fundamental Rust concept.
There are many questions that ask the same thing in regards to threads, which have similar restrictions. A small sampling:
Lifetime of variables passed to a new thread
How do I use static lifetimes with threads?
The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want
Lifetime troubles sharing references between threads
Like in those cases, you are attempting to share a reference to a variable with something that may exist after the variable is destroyed. Languages such as C or C++ would let you do this, only to have your program crash at a seemingly random point in time in the future when that variable is accessed after being freed. The crash is the good case, by the way; information leaks or code execution is also possible.
Like the case for threads, you have to ensure that this doesn't happen. The easiest way is to move the variable into the future, not sharing it at all. Another option is to use something like an Arc around your variable, clone the Arc and hand the clone to the future.
The tokio::fs::File::open(path: T + 'static) requires a 'static lifetime on its path parameter.
This makes sense because it is handled in runtime threads during the program's execution. I think it would make more sense if you could pass your own lifetimes, because the runtime does not need to run the whole time and so you could throw away some stuff. Do I understand something wrong?
I'd like to stay for 'static at the moment and so my problem is this...
I have a trait TraitN and some struct StructX { path: String, } with a fn new(path: &String) -> Box<TraitN>. The new creates and sets self.path = path.to_string();.
In some impl fn doit(&self) { ... } for StructX, I'd like to call tokio::fs::File::open(&self.path).
How can I pass &self.path with a 'static lifetime?
This is a complete example:
extern crate futures;
extern crate tokio;
#[macro_use]
extern crate error_chain;
use futures::future;
use futures::future::{loop_fn, ok, Future, Loop};
use futures::Stream;
use std::io::BufReader;
use tokio::{fs, io};
mod error {
error_chain!{}
}
use error::*;
type FutureResult<T> = future::FutureResult<T, Error>;
trait HandlerTrait {
fn new(path: &str) -> Box<HandlerTrait>
where
Self: Sized;
fn get_all(&self) -> FutureResult<Vec<String>>;
}
#[derive(Debug)]
pub struct Handler {
path: String,
}
impl HandlerTrait for Handler {
fn new(path: &str) -> Box<HandlerTrait> {
Box::new(Handler {
path: path.to_string(),
})
}
fn get_all(&self) -> FutureResult<Vec<String>> {
let file = fs::File::open(self.path.clone())
.and_then(|file: fs::File| ok(file))
.wait()
.unwrap();
let lines = io::lines(BufReader::new(file));
ok(lines
.filter(|line| line.len() > 80)
.map(|all| all[0..80].to_string())
.collect()
.wait()
.unwrap())
}
}
fn get_handler(path: &str) -> Option<Box<HandlerTrait>> {
Some(Handler::new(path))
}
fn get_path() -> FutureResult<String> {
ok("./somepath/file".to_string())
}
fn start_runtime() -> Result<()> {
let path: &str = get_path().wait().unwrap().as_str();
tokio::run(doit(path.clone()));
Ok(())
}
fn doit(path: &'static str) -> impl Future<Item = (), Error = ()> + 'static {
let n = 0;
loop_fn(n, move |_nr| {
let lh = get_handler(path).unwrap();
lh.get_all()
.or_else(|_| Err(()))
.and_then(|_all| ok(Loop::Break(())))
})
}
#[test]
fn test() {
start_runtime().unwrap();
assert!(true);
}
error[E0597]: borrowed value does not live long enough
--> src/lib.rs:63:22
|
63 | let path: &str = get_path().wait().unwrap().as_str();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
= note: borrowed value must be valid for the static lifetime...
playground
TL;DR Use a String instead of a &str. This might change when async / await syntax is stabilized.
Here's the MCVE I made of your original question:
extern crate tokio; // 0.1.11
trait TraitN {}
struct StructX {
path: String,
}
impl TraitN for StructX {}
fn new(path: &str) -> Box<TraitN> {
Box::new(StructX {
path: path.to_string(),
})
}
impl StructX {
fn doit(&self) {
tokio::fs::File::open(self.path.clone());
}
}
To solve this, clone the String and give ownership of it to the function:
impl StructX {
fn doit(&self) {
tokio::fs::File::open(self.path.clone());
}
}
With your example code, there are numerous problems:
fn start_runtime() -> Result<()> {
let path: &str = get_path().wait().unwrap().as_str();
tokio::run(doit(path.clone()));
Ok(())
}
You cannot take a reference to the result of unwrap because nothing will own that value. You cannot have a reference to this kind of temporary.
Cloning a &'a str returns a &'a str, not a String.
It doesn't make sense to call wait on the value because that blocks the thread. Run everything in the reactor loop.
This function should look like
fn start_runtime() -> Result<()> {
tokio::run({
get_path()
.map_err(|e| panic!("{}", e))
.and_then(|path| doit(path))
});
Ok(())
}
Then all of your code should switch to impl Into<String> instead of &str of &'static str. doit also needs to be able to create duplicate Strings:
fn doit(path: impl Into<String> + Clone) -> impl Future<Item = (), Error = ()> + 'static {
let n = 0;
let path = path.into();
loop_fn(n, move |_nr| {
let lh = get_handler(path.clone()).unwrap();
lh.get_all()
.or_else(|_| Err(()))
.and_then(|_all| ok(Loop::Break(())))
})
}
this [...] is config which doesn't change [...] read from a configfile during app init.
In that case, create a singleton which will give you an effectively-static value:
extern crate lazy_static; // 1.1.0
use lazy_static::lazy_static;
lazy_static! {
static ref PATH: String = {
// Should be read from a file.
String::from("/the/path/to/the/thing")
};
}
Then change all of the values to &'static str:
#[derive(Debug)]
pub struct Handler {
path: &'static str,
}
impl HandlerTrait for Handler {
fn new(path: &'static str) -> Box<HandlerTrait> {
Box::new(Handler {
path
})
}
}
And take a reference to the singleton:
fn start_runtime() -> Result<()> {
tokio::run(doit(&PATH));
Ok(())
}
You can couple this with phimuemue's answer to get a &'static MyConfigStruct, which could then have a fn foo(&'static self) that is available.
There must be something wrong with a language if this becomes so difficult and needs mem-io multiple times.
You are partially correct. It's difficult to have maximally performant async code with today's Rust (1.30) because Rust wants to ensure memory safety above all else. This doesn't mean that the code is unperformant, just that there's a bit of room to do better.
Honestly, making clones here is unlikely to be a performance bottleneck, but it is annoying. That's where async and await syntax comes in. This will allow futures to more easily make use of references in an idiomatic Rust manner.
because the runtime does not need to run the whole time [...] Do I understand something wrong?
However, async and await still might not help you, as by default Tokio will run your future on a different thread. That's one of the primary reasons it requires a 'static bound. This prevents a Tokio thread from having a reference to a stack local that goes out of scope, introducing memory unsafety. This isn't a unique problem to Tokio, however.
See also:
How can I pass a reference to a stack variable to a thread?
The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want
Why is the bound `T: 'a` required in order to store a reference `&'a T`?
Why does the Rust compiler request I constrain a generic type parameter's lifetime (error E0309)?
Other bits
It appears that every single call to wait in this code is a misuse of futures. You may wish to re-read the Tokio docs to better understand understand how you are supposed to chain futures. If there's a wait call, it's usually at the end of everything, and even that is rare when using Tokio.
See also:
How do I synchronously return a value calculated in an asynchronous Future in stable Rust?
You can restrict the lifetime of &self:
impl StructX {
fn doit(&'static self) {
// here, we know that self and its members are 'static
}
}
If you do this, you may actually be better off to have StructX store the 'static borrow of the path in the first place (instead of a string).
I can answer this now by myself:
fn make_static_str<T>(s: T) -> &'static str where T: Into<String>
A solution with Arc<Mutex<String>> - The test fails because there is no file to read on playground.
I have this piece of code using futures v0.1:
impl ArcService for (Box<MiddleWare<Request>>, Box<ArcService>) {
fn call(&self, req: Request, res: Response) -> Box<Future<Item = Response, Error = Error>> {
box self.0.call(req).and_then(move |req| self.1.call(req, res))
}
}
pub trait ArcService: Send + Sync {
fn call(&self, req: Request, res: Response) -> Box<Future<Item = Response, Error = Error>>;
}
pub trait MiddleWare<T>: Sync + Send {
fn call<'a>(&'a self, param: T) -> Box<Future<Item = T, Error = Error> + 'a>;
}
type MiddleWareFuture<'a, I> = Box<Future<Item = I, Error = Error> + 'a>;
impl MiddleWare<Request> for Vec<Box<MiddleWare<Request>>> {
fn call(&self, request: Request) -> MiddleWareFuture<Request> {
self.iter()
.fold(box Ok(request).into_future(), |request, middleware| {
box request.and_then(move |req| middleware.call(req))
})
}
}
pub struct ArcRouter {
routes: HashMap<Method, Box<ArcService>>,
}
// Service implementation
impl hyper::Server::Service for ArcRouter {
type Response = Response;
type Request = Request;
type Error = hyper::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, req: Request) -> Box<Future<Item = Self::Response, Error = Self::Error>> {
if let Some(routeMatch) = self.matchRoute(req.path(), req.method()) {
let mut request: ArcRequest = req.into();
request.paramsMap.insert(routeMatch.params);
let response = routeMatch.handler //handler is ArcService
.call(request, ArcResponse::new())
.map(|res| res.into());
return box response;
}
// TODO: this should be handled by a user defined 404 handler
return box Ok(Response::new().with_status(StatusCode::NotFound)).into_future();
}
}
Note the lifetime parameter on Middleware — it is used to avoid lifetime issues.
This does not compile because Box<Future<Item = Response, Error = Error>> is implicitly 'static and therefore causes lifetime issues. hyper::Server::Service requires a 'static Future
Here is an example that aptly describes my problem:
extern crate futures; // v0.1 (old)
use futures::{future, Future};
struct Example {
age: i32,
}
// trait is defined in an external crate. You can't change it's definition
trait MakeFuture {
fn make_a_future(&self) -> Box<Future<Item = i32, Error = ()>>;
}
impl MakeFuture for Example {
fn make_a_future(&self) -> Box<Future<Item = i32, Error = ()>> {
let f = future::ok(self).map(|ex| ex.age + 1);
Box::new(f)
}
}
playground link
which gives the lifetime error:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:16:28
|
16 | let f = future::ok(self).map(|ex| ex.age + 1);
| ^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 15:5...
--> src/main.rs:15:5
|
15 | / fn make_a_future(&self) -> Box<Future<Item = i32, Error = ()>> {
16 | | let f = future::ok(self).map(|ex| ex.age + 1);
17 | | Box::new(f)
18 | | }
| |_____^
note: ...so that expression is assignable (expected &Example, found &Example)
--> src/main.rs:16:28
|
16 | let f = future::ok(self).map(|ex| ex.age + 1);
| ^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that expression is assignable (expected std::boxed::Box<futures::Future<Item=i32, Error=()> + 'static>, found std::boxed::Box<futures::Future<Item=i32, Error=()>>)
--> src/main.rs:17:9
|
17 | Box::new(f)
| ^^^^^^^^^^^
Is there a way to get around this? I'm building with hyper::Service and using Rust v1.25.0 (nightly)
How to return a future combinator with &self
You return a future that refers to self like this:
use futures::future::{self, FutureResult}; // 0.1.28
struct Example {
age: i32,
}
impl Example {
fn make_a_future(&self) -> FutureResult<&Example, ()> {
future::ok(self)
}
}
As discussed in the Tokio documentation on returning futures, the easiest stable solution to returning a complicated future is a impl Trait. Note that we assign an explicit lifetime to self and use that in the returned value (via + 'a):
use futures::{future, Future}; // 0.1.28
struct Example {
age: i32,
}
impl Example {
fn make_a_future<'a>(&'a self) -> impl Future<Item = i32, Error = ()> + 'a {
future::ok(self).map(|ex| ex.age + 1)
}
}
Your real question is "how can I lie to the compiler and attempt to introduce memory unsafety into my program?"
Box<SomeTrait + 'static> (or Box<SomeTrait> by itself) means that the trait object must not contain any references that do not last for the entire program. By definition, your Example struct has a shorter lifetime than that.
This has nothing to do with futures. This is a fundamental Rust concept.
There are many questions that ask the same thing in regards to threads, which have similar restrictions. A small sampling:
Lifetime of variables passed to a new thread
How do I use static lifetimes with threads?
The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want
Lifetime troubles sharing references between threads
Like in those cases, you are attempting to share a reference to a variable with something that may exist after the variable is destroyed. Languages such as C or C++ would let you do this, only to have your program crash at a seemingly random point in time in the future when that variable is accessed after being freed. The crash is the good case, by the way; information leaks or code execution is also possible.
Like the case for threads, you have to ensure that this doesn't happen. The easiest way is to move the variable into the future, not sharing it at all. Another option is to use something like an Arc around your variable, clone the Arc and hand the clone to the future.
I am trying to create a custom error type to use in my Rust project by implementing std::error::Error. I've also created a small shortcut function to create std::io::Error. Unfortunately, I'm stuck with lifetimes so I'm asking some help:
use std::error::Error;
use std::fmt;
use std::io;
#[derive(Debug)]
pub struct BadString<'a> {
desc: &'a str,
}
impl<'a> BadString<'a> {
pub fn new(desc: &str) -> BadString {
BadString{ desc: desc }
}
}
impl<'a> Error for BadString<'a> {
fn description(&self) -> &str { &self.desc }
}
impl<'a> fmt::Display for BadString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.description())
}
}
fn bad_str_err(desc: &str) -> io::Error {
let err = BadString::new(desc);
io::Error::new(io::ErrorKind::Other, err)
}
fn main() {
}
playground
This reports the error:
<anon>:27:30: 27:34 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
<anon>:27 let err = BadString::new(desc);
^~~~
<anon>:28:5: 28:46 note: first, the lifetime cannot outlive the call at 28:4...
<anon>:28 io::Error::new(io::ErrorKind::Other, err)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<anon>:28:42: 28:45 note: ...so that argument is valid for the call
<anon>:28 io::Error::new(io::ErrorKind::Other, err)
^~~
<anon>:27:30: 27:34 note: but, the lifetime must be valid for the expression at 27:29...
<anon>:27 let err = BadString::new(desc);
^~~~
<anon>:27:30: 27:34 note: ...so that auto-reference is valid at the time of borrow
<anon>:27 let err = BadString::new(desc);
^~~~
error: aborting due to previous error
playpen: application terminated with error code 101
I am not sure how to fix that so it will compile.
Let's look at the signature of io::Error::new:
fn new<E>(kind: ErrorKind, error: E) -> Error
where E: Into<Box<Error + Send + Sync>>
This states that error can be any type, so long as that type implements the trait Into<Box<Error + Send + Sync>>. That trait means that the type can be converted into a boxed trait object. The trait object itself must implement the traits Error, Send and Sync. What's non-obvious is that by default, trait objects also have a 'static lifetime bound (there's rationale for this, but it does seem to trip people up).
Let's try to do that conversion ourselves:
fn bad_str_err(desc: &str) -> io::Error {
let err = BadString::new(desc);
let foo: Box<Error + Send + Sync + 'static> = err.into();
}
And we get the same error — "cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements". So our problem lies in the ability to convert to this trait object.
Send and Sync are two key traits that help guide the compiler to know which types are safe to send / share between threads. In order for something to be safely shared across threads, it cannot "disappear" while another thread has it. This is a type of bug that Rust helps prevent at compile time.
In this case, you are tying to use a string slice (&str), but that slice doesn't own the underlying memory, it just references it. As soon as that memory goes out of scope, any references need to become invalid. This is another thing that Rust prevents at compile time.
In this case, the simplest thing to do is to not use a reference:
use std::error::Error;
use std::{fmt, io};
#[derive(Debug)]
pub struct BadString {
desc: String,
}
impl BadString {
pub fn new(desc: &str) -> BadString {
BadString { desc: desc.into() }
}
}
impl Error for BadString {
fn description(&self) -> &str { &self.desc }
}
impl fmt::Display for BadString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.description())
}
}
fn bad_str_err(desc: &str) -> io::Error {
let err = BadString::new(desc);
io::Error::new(io::ErrorKind::Other, err)
}
fn main() {}
A String owns the underlying memory, so it can safely be transferred across thread boundaries and doesn't need to worry about any other object being freed accidentally.