I'm designing a connection retry function. It retries if the error was ConnectionClosed. In the full implementation it has other things that justify it being a recursion, but this is off topic. Anyways, since it's a connection, it makes sense to be async. However, async recursives force me to use BoxFuture, and we know that asyncs capture and return all the arguments.
use std::future::Future;
use futures::future::{BoxFuture, FutureExt};
struct Message;
struct Client;
enum Error {
ConnectionClosed
}
impl Client {
fn send_and_expect<'a>(
&'a mut self,
message: &'a Message
) -> BoxFuture<'a, Result<(), Error>> {
async move {
Ok(())
}.boxed()
}
//With this function I can wrap any async function f that grabs data from the internet
pub fn connection_retrier<'a, T>(
f: fn(&'a mut Self, &'a Message) -> T,
f_self: &'a mut Self,
f_m: &'a Message,
) -> BoxFuture<'a, Result<(),Error>>
where
T: Future<Output = Result<(), Error>> + 'a + Send
{
/*
By making f: fn(&'a mut Self, &'a Message) -> T, we tell the compiler that
`f` is a specific function: one that specifically requires the `Self` and `Message`
arguments to live as long as `'a`, where `'a` is the min of the lifetimes of the `f_self` and `f_m`
arguments passed to `connection_retrier`.
Thus this forces `f_self` and `f_m` to live as long as `connection_retrier`.
The call `let r = f(f_self, f_m).await` returns a `Future` that lives as long as `'a`.
I think this is the problem. The result of the first call borrows `f_self` and `f_m`
for at least the entire lifetime of `r`. This would make it impossible to use
`f_self` and `f_m` again inside `connection_retrier`. As you see, I tried making sure
that `r` destructs before we use `f_self` in `connection_retrier` again, but somehow
`r` is marked to live as long as `connection_retrier`, because `f_self` is still considered
to be borrowed.
*/
async move {
let ok: bool;
{
let r = f(f_self, f_m).await;
match r {
Ok(_) => ok = true,
Err(Error::ConnectionClosed) => {
ok = false;
}
}
}
match ok {
true => Ok(()),
false => Client::connection_retrier(f, f_self, f_m).await
}
}.boxed()
}
async fn send_with_retry<'a> (
&'a mut self,
message: &'a Message,
) -> Result<(), Error>
{
Client::connection_retrier(
Client::send_and_expect,
self,
message
).await
}
}
Error:
error[E0499]: cannot borrow `*f_self` as mutable more than once at a time
--> src/lib.rs:58:56
|
24 | f: fn(&'a mut Self, &'a Message) -> T,
| - lifetime `'1` appears in the type of `f`
...
48 | let r = f(f_self, f_m).await;
| --------------
| | |
| | first mutable borrow occurs here
| argument requires that `*f_self` is borrowed for `'1`
...
58 | false => Client::connection_retrier(f, f_self, f_m).await
| ^^^^^^ second mutable borrow occurs here
Playground
I understand why the error occurs: Client::connection_retrier(f, f_self, f_m).await, let's call it r, holds a mutable reference to f_self, so I cannot use it again while it's being held. However, after I check that this result r is Error::ConnectionClosed, I don't need it anymore, so there should be a way to discard it so I can reborrow it mutably.
The problem
As #moy2010 said, the problem is here:
pub fn connection_retrier<'a, T>(
f: fn(&'a mut Self, &'a Message) -> T,
f_self: &'a mut Self,
f_m: &'a Message,
) -> BoxFuture<'a, Result<(), Error>>
where T: Future<Output = Result<(), Error>> + 'a + Send
This signature defines f to be a function that accepts arguments with exactly the lifetime 'a, which is alive throughout the body of connection_retrier.
When you are able to temporarily pass a mutable reference to a function and then 'recover' it later, this is called a reborrow. Reborrowing from a mutable reference works by creating a new mutable reference of shorter lifetime to the data it points to, and preventing use of the original reference until this lifetime has ended. Usually a reborrow happens during a function call, in which case that lifetime is just long enough for the function call to be made.
As you can see, reborrowing always produces a reference with lifetime shorter than the original. But f needs a reference whose lifetime is exactly 'a, so the compiler correctly deduces that the call to f cannot reborrow f_self and instead, f_self is permanently moved into f. Hence the error when you attempt to use it again.
Solutions
If you can change the signature of send_and_expect so that the returned future only borrows from message (i.e. not the Client), then #moy2010 has already provided a correct solution in the comments to their answer. I will proceed on the assumption that send_and_expect must borrow from both its arguments, as indicated by the signature in the question.
Since you would like to reborrow f_self before passing it to f, what you really need is to define f to be a function that can be called for any lifetime (not just 'a). You might think that means we can just change the declaration of f to for<'b> fn(&'b mut Self, &'b Message) -> T. But wait! Your where bound specifies that T borrows from the lifetime 'a, not 'b, so this signature doesn't line up with that of send_and_expect. But also notice that we can't change the bound to say 'b instead, because 'b is not in scope outside of the declaration of f! So there is no way to express the correct bound on T here. There are, however, a few workarounds.
Use a Boxed trait object
The simplest solution is just to make the return type of f a boxed trait object instead of a generic parameter, like this:
pub fn connection_retrier<'a>(
f: for<'b> fn(&'b mut Self, &'b Message) -> BoxFuture<'b, Result<(), Error>>,
f_self: &'a mut Self,
f_m: &'a Message,
) -> BoxFuture<'a, Result<(), Error>>
Now we can specify what the return value borrows from inline, avoiding the problem that 'b isn't in scope for a where bound. Since send_and_expect already returns a BoxFuture anyway in your example, this may be the best solution for you.
Use a trait implemented on a ZST instead of a fn pointer
Resorting to boxing and dynamic dispatch to satisfy a compile-time issue might strike some as distasteful, and there is a small performance penalty. This is a trick that can be used to make this work with static dispatch, at the cost of some verbosity. We will have to define our own trait, ConnectFn:
trait ConnectFn<'a> {
type ConnectFuture: Future<Output=Result<(), Error>> + Send + 'a;
fn connect(&self, client: &'a mut Client, message: &'a Message) -> Self::ConnectFuture;
}
Then, we can make the signature of connection_retrier the following:
pub fn connection_retrier<'a, F>(
f: F,
f_self: &'a mut Self,
f_m: &'a Message,
) -> BoxFuture<'a, Result<(), Error>>
where F: for<'b> ConnectFn<'b>
And replace f(f_self, f_m) in the body with f.connect(f_self, f_m). This avoids the need for the return type of f to appear anywhere in connection_retrier's signature, circumventing the issue we had before.
We can then replace our function send_and_expect with a zero-sized type SendAndExpect which implements ConnectFn, like so:
struct SendAndExpect;
impl<'a> ConnectFn<'a> for SendAndExpect
{
type ConnectFuture = BoxFuture<'a, Result<(), Error>>;
fn connect(&self, client: &'a mut Client, message: &'a Message) -> Self::ConnectFuture {
/* the body of Client::send_and_expect goes here. `self` is ignored */
}
}
With this, we can call connection_retrier like so:
Client::connection_retrier(SendAndExpect, &mut client, &message)
Which is pretty nice. The definition of SendAndExpect itself can be made even nicer, close to that of a regular fn, using macros, but this is left as an exercise to the reader.
Sidenote: You might think that it would be possible to have a blanket implementation of ConnectFn on suitable fn pointers and avoid the need for a new type, but sadly you would be wrong. You'll either run into the same issue with impossible where clause bounds or trait solver limitations when dealing with universally quantified types.
The reason lies within these three lines:
pub fn connection_retrier<'a, T>(
f: fn(&'a mut Self, &'a Message) -> T,
f_self: &'a mut Self,
For f, you are telling the compiler that it will receive a mutable reference with a lifetime equal to that of the mutable reference that connection_retrier receives for f_self (in this case it is 'a).
When you change f's signature to f: fn(&mut Self, &Message) -> T, you are letting the compiler determine the lifetime, which it correctly does by assigning it a lifetime shorter than 'a and hence why it compiles.
Related
I am seeking help to understand why the borrow checker fails for the following minimal non-working example, and I would be very happy to learn how to correctly implement what I was trying to do:
use std::collections::HashSet;
struct Foo {
data: HashSet<usize>
}
impl Foo {
fn test<'a, F, T>(&mut self, _operation: F) -> ()
where F: Fn(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
T: Iterator<Item=&'a usize>
{
let update: HashSet<usize> = vec![4, 2, 9].into_iter().collect();
self.data = _operation(&self.data, &update).copied().collect();
}
fn new() -> Self {
Foo { data: HashSet::new() }
}
}
fn main() {
let mut foo: Foo = Foo::new();
foo.test(HashSet::intersection);
}
My main source of confusion is that, if I replace the call to _operation with HashSet::intersection, the code compiles. I thought that the type of the parameter _operation would allow me to pass both HashSet::intersection and HashSet::union as operations here.
For the record, this is the error I receive:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src\main.rs:13:32
|
13 | self.data = _operation(&self.data, &update).copied().collect();
| ^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 8:23...
--> src\main.rs:8:23
|
8 | fn test<'a, F, T>(&mut self, _operation: F) -> ()
| ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src\main.rs:13:32
|
13 | self.data = _operation(&self.data, &update).copied().collect();
| ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the method body at 8:13...
--> src\main.rs:8:13
|
8 | fn test<'a, F, T>(&mut self, _operation: F) -> ()
| ^^
note: ...so that reference does not outlive borrowed content
--> src\main.rs:13:32
|
13 | self.data = _operation(&self.data, &update).copied().collect();
| ^^^^^^^^^^
For more information about this error, try `rustc --explain E0495`.
error: could not compile `aoc06` due to previous error
The issue, as the (albeit cryptic) compiler message suggests, is that there is a lifetime mismatch: _operation expects HashSet references that live as long as 'a, but &self.data has a lifetime 'b, the elided lifetime of &mut self, and &update has a different lifetime that lasts the duration of the test function body.
To resolve this issue, we must specify that the function type F takes in HashMap references of arbitrary lifetimes, not just the specific lifetime 'a -- this lets the compiler infer the appropriate lifetime when _operation is invoked. This is why we need Higher-Rank Trait Bounds (HRTBs):
fn test<F, T>(&mut self, _operation: F) -> ()
where F: for<'a> Fn(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
However, this raises another issue. How do we apply the higher-ranked lifetime 'a to the type parameter T? Unfortunately Rust does not support higher-kinded types, but we can get away with "abstracting" out the the function type F and the higher-kinded type T to a trait and an associated type on said trait.
trait Operation<'a, T: 'a> {
type Output: Iterator<Item = &'a T>;
fn operate(self, a: &'a HashSet<T>, b: &'a HashSet<T>) -> Self::Output;
}
The Operation trait represents an operation on two HashSets that returns an iterator over references, equivalent to the functions HashSet::union, HashSet::intersection, and the like. We can achieve this using the following impl, which ensures that HashSet::intersection and the like implement Operation:
impl<'a, T: 'a, I, F> Operation<'a, T> for F
where
I: Iterator<Item = &'a T>,
F: FnOnce(&'a HashSet<T>, &'a HashSet<T>) -> I,
{
type Output = I;
fn operate(self, a: &'a HashSet<T>, b: &'a HashSet<T>) -> Self::Output {
self(a, b)
}
}
We can then use a HRTB on the Operation trait instead, which does not require any nested higher-kinded types:
fn test(&mut self, _operation: impl for<'a> Operation<'a, usize>) -> () {
let update: HashSet<usize> = vec![4, 2, 9].into_iter().collect();
self.data = _operation.operate(&self.data, &update).copied().collect();
println!("{:?}", self.data);
}
Playground
The arguments you are passing do not match the lifetime which you declared the Fn bound with.
fn test<'a, F, T>(&mut self, _operation: F) -> ()
'a is some arbitrary lifetime that may be specified by the caller,
F: Fn(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
which must be adequate for the references given to _operation,
let update: HashSet<usize> = vec![4, 2, 9].into_iter().collect();
self.data = _operation(&self.data, &update).copied().collect();
but here you pass in a borrow from self (whose lifetime is not specified to outlive 'a) and a borrow from update (which is a local variable, which cannot outlive 'a).
In order to correctly write this, you need to specify that _operation may be called with any lifetime (which thus includes the lifetimes of borrows of local variables). That's simple, by itself:
fn test<F, T>(&mut self, _operation: F) -> ()
where
F: for<'a> Fn(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
Note that 'a is no longer a lifetime parameter of test. Instead it's part of the bound on F: you can read this for<'a> notation as “for any lifetime, which we will call 'a, F may be called as a function with references &'a ...”.
However, this isn't actually a solution, because you also have T: Iterator<Item = &'a usize>, which uses 'a again. It isn't currently possible to write a where clause that expresses this relationship, particularly as even without the item being a reference, the iterator will be borrowing the &'a HashSets.
This is an unfortunate limitation of current Rust — it also comes up in trying to write a function that takes an async function which borrows an input (which is structurally the same as your situation, with Future in place of Iterator). However, there is a workaround: you can define a trait for the function, which has a single lifetime parameter that links everything together. (This doesn't impose any extra work on the caller of your function, because the trait is blanket implemented for all suitable functions.)
Here's your code with such a trait added and the needed modifications to fn test():
use std::collections::HashSet;
trait IteratorCallback<'a> {
type Output: Iterator<Item = &'a usize> + 'a;
fn call(self, a: &'a HashSet<usize>, b: &'a HashSet<usize>) -> Self::Output;
}
impl<'a, F, T> IteratorCallback<'a> for F
where
F: FnOnce(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
T: Iterator<Item = &'a usize> + 'a,
{
type Output = T;
fn call(self, a: &'a HashSet<usize>, b: &'a HashSet<usize>) -> T {
// Delegate to FnOnce
self(a, b)
}
}
struct Foo {
data: HashSet<usize>,
}
impl Foo {
fn test<F>(&mut self, _operation: F) -> ()
where
F: for<'a> IteratorCallback<'a>,
{
let update: HashSet<usize> = vec![4, 2, 9].into_iter().collect();
self.data = _operation.call(&self.data, &update).copied().collect();
}
fn new() -> Self {
Foo {
data: HashSet::new(),
}
}
}
fn main() {
let mut foo: Foo = Foo::new();
foo.test(HashSet::intersection);
}
Note: I changed the function bound to FnOnce because that's more permissive than Fn and all you need in this case, but the same technique will work with Fn as long as you change fn call(self, to fn call(&self,.
Credit: I used this Reddit comment by user Lej77 as an example to work from for the trait technique.
I reached this error in this example, and I'm trying to understand what's happening.
use futures::future::{BoxFuture, FutureExt};
use std::sync::Arc;
use futures::lock::Mutex;
struct Message {}
struct Client {}
enum Error {
ConnectionClosed,
}
#[derive(Default)]
struct MyType{}
impl Client {
fn send_and_expect<'a, R: 'a + Default + Send>(
client: Arc<Mutex<Self>>,
message: &'a Message,
) -> BoxFuture<'a, Result<R, Error>> {
async move { Ok(R::default()) }.boxed()
}
pub fn connection_retrier<'a, R: 'a + Default + Send>(
client: Arc<Mutex<Self>>,
f: for<'b> fn(Arc<Mutex<Self>>, &'b Message) -> BoxFuture<'b, Result<R, Error>>,
f_m: &'a Message,
) -> BoxFuture<'a, Result<R, Error>>
{
async move {Ok(R::default())}.boxed()
}
async fn send_with_retry<'a>(
client: Arc<Mutex<Self>>,
message: &'a Message,
) -> Result<MyType, Error> {
let client = Arc::new(Mutex::new(Client{}));
Self::connection_retrier::<MyType>(client, Self::send_and_expect, message)
.await
}
}
Error:
error[E0308]: mismatched types
--> src/lib.rs:36:52
|
36 | Self::connection_retrier::<MyType>(client, Self::send_and_expect, message)
| ^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'b> fn(Arc<_>, &'b Message) -> Pin<Box<(dyn futures::Future<Output = std::result::Result<MyType, Error>> + std::marker::Send + 'b)>>`
found fn pointer `fn(Arc<_>, &Message) -> Pin<Box<dyn futures::Future<Output = std::result::Result<MyType, Error>> + std::marker::Send>>`
Playground
Well, connection_retrier requres a function f: for<'b> fn(Arc<Mutex<Self>>, &'b Message) -> BoxFuture<'b, Result<R, Error>>,. The one I passed seems to fit the signature.
The way I view for<'b> fn... is: I accept a function f for any lifetime 'b presented. So, for the particular lifetime 'a, send_and_expect seems to fit everything.
The only suspicion I have is for the lifetime of R being fixed at 'a in connection_retrier so the f with its for<'b> is not free to give any lifetime for its R. I think the lifetime of the R in f should be 'b. Is it possible to force f to have R with lifetime 'b?
Something like this:
f: for<'b> fn<RR: 'b>(Arc<Mutex<Self>>, &'b Message) -> BoxFuture<'b, Result<RR, Error>>,
This error looks to be caused by limitation of the compiler's ability to automatically convert generic functions which include a type bounded by a lifetime into a function pointer that is fully generic over that lifetime. I'm not quite sure why this limitation exists, but it can be worked around by wrapping the generic call in a non-generic closure. All you need to do to make your example compile is to change:
Self::send_and_expect
to
|c, m| Self::send_and_expect(c, m)
Thus hiding the generic from the compiler.
I'm trying to create a struct that will manage a Tokio task with one tokio::sync::mpsc::Sender that sends input to the task, one tokio::sync::mpsc::Receiver that receives output from the task, and a handle that I can join at the end.
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
// A type that implements BlockFunctionality consumes instances of T and
// produces either Ok(Some(U)) if an output is ready, Ok(None) if an output
// is not ready, or an Err(_) if the operation fails
pub trait BlockFunctionality<T, U> {
fn apply(&mut self, input: T) -> Result<Option<U>, &'static str>;
}
pub struct Block<T, U> {
pub tx_input: mpsc::Sender<T>,
pub rx_output: mpsc::Receiver<U>,
pub handle: JoinHandle<Result<(), &'static str>>,
}
impl<T: Send, U: Send> Block<T, U> {
pub fn from<B: BlockFunctionality<T, U> + Send>(b: B) -> Self {
let (tx_input, mut rx_input) = mpsc::channel(10);
let (mut tx_output, rx_output) = mpsc::channel(10);
let handle: JoinHandle<Result<(), &'static str>> = tokio::spawn(async move {
let mut owned_b = b;
while let Some(t) = rx_input.recv().await {
match owned_b.apply(t)? {
Some(u) => tx_output
.send(u)
.await
.map_err(|_| "Unable to send output")?,
None => (),
}
}
Ok(())
});
Block {
tx_input,
rx_output,
handle,
}
}
}
When I try to compile this, I get this error for B and a similar one for the other two type parameters:
|
22 | pub fn from<B: BlockFunctionality<T, U> + Send>(b:B) -> Self {
| -- help: consider adding an explicit lifetime bound...: `B: 'static +`
...
27 | let handle:JoinHandle<Result<(), &'static str>> = tokio::spawn(async move {
| ^^^^^^^^^^^^ ...so that the type `impl std::future::Future` will meet its required lifetime bounds
I'm having a hard time understanding where the problem is with lifetimes. The way I understand it, lifetime problems usually come from references that don't live long enough, but I'm moving values, not using references. I move b, rx_input, and tx_output into the closure and I keep tx_input, rx_output, and handle in the calling scope. Does anyone know how to satisfy the compiler in this case?
Those values may be references or contain references. Reference types are valid types: B could be &'a str. Or B could be SomeType<'a>, a type with a lifetime parameter, that itself contains a &'a str.
To say that B: 'static means that all lifetime parameters of B outlive 'static (ref). For example, types which own their own data and thus have no lifetime parameters (e.g. String) satisfy this bound. But &'static str also satisfies the bound.
Because tokio::spawn creates something whose lifetime is not statically scoped, it requires a 'static argument.
So to satisfy the compiler, add the 'static bounds:
impl<T: 'static + Send, U: 'static + Send> Block<T, U> {
pub fn from<B: 'static + BlockFunctionality<T, U> + Send>(b:B) -> Self {
I have a function that requires an asynchronous callback (a request handler); I'm currently trying to accept things that look like this:
async fn handle_request<'a>(request: Request, body: &'a mut (dyn AsyncRead + 'a)) -> HandlerResponse
It had been working up until the addition of the second parameter body, which is causing me grief. The function that accepts the parameter looks like this:
pub async fn process_requests<H, F>(
mut connection: Box<dyn AsyncConnection>,
request_handler: &H,
) -> Result<(), DecodeError>
where
for<'a> H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a,
F: Future<Output = HandlerResponse>,
{
Part way through this function, we call a helper function:
handle_request(&mut connection, request_handler, request)
which has a very similar signature; in particular, the signature for request_handler is identical. It does some minor pre-processing before invoking request_handler. When I attempt to compile this, I get:
error[E0310]: the parameter type `H` may not live long enough
|
106 | pub async fn process_requests<H, F>(
| - help: consider adding an explicit lifetime bound `H: 'static`...
...
142 | handle_request(&mut connection, request_handler, request)
| ^^^^^^^^^^^^^^
|
note: ...so that the type `H` will meet its required lifetime bounds
|
142 | handle_request(&mut connection, request_handler, request)
| ^^^^^^^^^^^^^^
Why do I need this / what do I do about this? Indeed adding 'static to H: in the where does seem to silence the error, but is that the right thing to do? Couldn't the type implementing H carry a reference, and 'static would forbid that? I don't necessarily want to do that, but any amount of trying to annotate a lifetime that isn't 'static onto H has not worked; e.g., 'a + Fn(...) -> F + 'a does not work, nor does adding a new generic 'b lifetime. I'd rather not 'static something that doesn't need it, but I don't see how to do that.
(I'm also a bit perplexed by the wording of the message — that the parameter type — not some argument or variable — doesn't live long enough. How does a type not live long enough?)
I've played with things a bit more, but I still can't get anything that actually compiles. This playground example shows another one of the more perplexing error messages that I'm running into. I've tried dropping some of the lifetime annotations, and moved the for<'a> bit (I'm not sure what the difference is?).
A callback argument passed as reference does not work with HRTB constraints when the callback is marked with the async keyword.
The signature using async/await:
async fn handle_request<'a>(request: Request, body: &'a mut (dyn AsyncRead + 'a)) -> HandlerResponse
Is equivalent to:
fn handle_request<'a>(request: Request, body: &'a mut (dyn AsyncRead + 'a)) -> Future<Output=HandlerResponse> + 'a
This implies that input lifetimes of an async function are captured in the
future returned by the async function.
See the paragraph "Lifetime capture in the anonymous future" of RFC 2394.
Declaring the function that accepts the parameter as:
pub async fn process_requests<H, F>(
mut connection: Box<dyn AsyncConnection>,
request_handler: &H,
) -> Result<(), DecodeError>
where
for<'a> H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a,
F: Future<Output = HandlerResponse>,
{
Give a compilation error because the HRTB requirement:
for<'a> H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a
"unlink" the lifetime bound from the caller and produce the compilation error
expected bound lifetime parameter 'a, found concrete lifetime
for more details about HRTB see here.
To make it works you have to write:
pub async fn process_requests<'a, H, F>(
mut connection: Box<dyn AsyncConnection>,
request_handler: &H,
) -> Result<(), DecodeError>
where
H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a,
F: Future<Output = HandlerResponse>,
{
But this get you to another problem:
`body` does not live long enough
because the local body struct does not outlive request_handler:
async fn handle_request<'a, H, F>(
request_handler: &H,
request: Request,
) -> io::Result<()>
where
H: Fn(Request, &'a mut (dyn AsyncRead + 'a)) -> F,
F: Future<Output = String>,
{
let mut body = Body {};
request_handler(request, &mut body);
unimplemented!();
}
If feasible
one possible solution could be to use Boxed trait objects and get rid off HTRB constraints.
I'm attempting to have a trait for things that can either simply contain other things, or create them on demand, given a thing's name. Those contained things should in turn be able to do the same, creating a hierarchy of sorts. Here's a minimal code:
use std::ops::Deref;
pub enum BoxOrRef<'a, T: ?Sized + 'a> {
Boxed(Box<T>),
Ref(&'a T),
}
impl<'a, T: ?Sized + 'a> Deref for BoxOrRef<'a, T> {
type Target = T;
fn deref(&self) -> &T {
match self {
BoxOrRef::Boxed(b) => &b,
BoxOrRef::Ref(r) => r,
}
}
}
pub trait Elem {
fn get_subelem<'a, 'b>(&'a self, name: &'b str) -> Option<BoxOrRef<'a, dyn Elem>>;
}
pub trait Table {
fn get_elem<'a, 'b>(&'a self, name: &'b str) -> Option<BoxOrRef<'a, dyn Elem>>;
}
fn resolve_name<'a, T: Table + ?Sized>(
table: &'a T,
name: &[String],
) -> Option<BoxOrRef<'a, dyn Elem>> {
let mut segments = name.iter();
if let Some(first_segment) = segments.next() {
segments.fold(table.get_elem(&first_segment), |res, next| {
res.and_then(|elem| elem.get_subelem(next))
})
} else {
None
}
}
The lifetime checker however, is not satisfied by this:
error[E0597]: `elem` does not live long enough
--> src/lib.rs:33:33
|
33 | res.and_then(|elem| elem.get_subelem(next))
| ^^^^ - borrowed value only lives until here
| |
| borrowed value does not live long enough
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 26:17...
--> src/lib.rs:26:17
|
26 | fn resolve_name<'a, T: Table + ?Sized>(
| ^^
I need to somehow extend lifetimes of the intermediate res's. I guess I could put them in a struct and tweak the return type of resolve_name to return it along with the final element, but that strikes me as rather clumsy way of doing it. Is there a better solution?
The return value of get_subelem can't outlive the &self borrow you used to call it, because the signature of get_subelem says so explicitly:
fn get_subelem<'a, 'b>(&'a self, name: &'b str) -> Option<BoxOrRef<'a, dyn Elem>>;
// ^^ ^^
In order to get a BoxOrRef<'a, _>, you have to borrow self for the lifetime 'a. In the caller, elem can't outlive the closure it belongs to, and get_subelem borrows elem, so it can't return a value that can escape that closure either.
You're trying to do something that is unsafe, and the compiler is right to stop you. In theory, table.get_elem could return a Boxed value, and elem.get_subelem could return an internal reference, and then the Box would be dropped when the closure returns, invalidating the reference.
Presumably that doesn't actually happen, so you have to tell the compiler that. One way is to decouple &self from BoxOrRef<'a, _>:
pub trait Elem<'a> {
fn get_subelem(&self, name: &str) -> Option<BoxOrRef<'a, dyn Elem<'a>>>;
}
The above change will make your example compile once you add lifetime parameters to all the Elems, but it puts you in an awkward position when implementing Elem: you can't return a reference to self, so practically everything has to be Boxed.
It's hard to make a good recommendation given the vagueness of the example, but I suggest you take a step back and think about whether BoxOrRef is the right abstraction here. Fundamentally, you can't do anything with a BoxOrRef that you couldn't do with a reference, because the BoxOrRef might be a reference. At the same time, you can't do anything with it that you couldn't do with a Box, because it might be a Box. std::borrow::Cow uses ToOwned to implement Clone and into_owned -- perhaps a similar approach could work for you. (And if you can, maybe just implement ToOwned for dyn Elem and use Cow directly.)