Reputation: 585
I have a trait that returns a borrow attached to its own lifetime:
trait SomeTrait {
fn do<'a>(&'a self, other: &AnonymousLifetime) -> &'a Output;
}
How can this same restriction be expressed in a where clause for a closure, so that SomeTrait
can impl From<Closure>
?
A minimal, reproducible example for the scenario (playground):
// The traits
trait Context {
fn give(&self) -> usize;
}
trait ContextDecider {
fn decide<'a>(&'a self, context: &dyn Context) -> &'a str;
}
// A concrete implementation example
// As expected, works OK
struct SomeDecider(Vec<String>);
impl ContextDecider for SomeDecider {
fn decide<'a>(&'a self, context: &dyn Context) -> &'a str {
let some_context = context.give();
if some_context > self.0.len() {
panic!("Oh no!");
}
&self.0[some_context]
}
}
// An implemetation for a closure
// Help here!!
impl<'a, F> ContextDecider for F
where
F: 'a + Fn(&dyn Context) -> &'a str,
{
fn decide<'b>(&'b self, giver: &dyn Context) -> &'b str {
self(giver)
}
}
Fails to compile:
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/lib.rs:30:9
|
30 | self(giver)
| ^^^^^^^^^^^
|
note: ...the reference is valid for the lifetime `'b` as defined on the method body at 29:15...
--> src/lib.rs:29:15
|
29 | fn decide<'b>(&'b self, giver: &dyn Context) -> &'b str {
| ^^
note: ...but the borrowed content is only valid for the lifetime `'a` as defined on the impl at 25:6
--> src/lib.rs:25:6
|
25 | impl<'a, F> ContextDecider for F
| ^^
In the example, I am failing to express in the closure bounds the restriction that the trait imposes and the compiler is not happy.
The compiler is not helping me with what syntax I should use that will allow me to lock in the two lifetimes together.
Upvotes: 1
Views: 146
Reputation: 36031
You could wrap the function in another struct (here called FnWrapper
):
trait Context {
fn give(&self) -> usize;
}
impl Context for () {
fn give(&self) -> usize {0}
}
trait ContextDecider {
fn decide<'a>(&'a self, context: &dyn Context) -> &'a str;
}
struct SomeDecider(Vec<String>);
impl ContextDecider for SomeDecider {
fn decide<'a>(&'a self, context: &dyn Context) -> &'a str {
let some_context = context.give();
if some_context > self.0.len() {
panic!("Oh no!");
}
&self.0[some_context]
}
}
struct FnWrapper<'a, F> {
f: F,
_phantom: std::marker::PhantomData<& 'a ()>,
}
impl<'a, F> FnWrapper<'a, F>
where F: 'a + Fn(&dyn Context) -> &'a str,
{
fn new(f: F) -> Self {
Self {
f,
_phantom: Default::default(),
}
}
fn f_decide<'b>(&'b self, giver: &dyn Context) -> &'b str {
(self.f)(giver)
}
}
impl<'a, F> ContextDecider for FnWrapper<'a, F>
where
F: 'a + Fn(&dyn Context) -> &'a str,
{
fn decide<'b>(&'b self, giver: &dyn Context) -> &'b str {
self.f_decide(giver)
}
}
fn main() {
println!("{}", FnWrapper::new(|giver| vec!["1", "2", "3"][giver.give()]).decide(&()));
}
Upvotes: 1
Reputation: 36031
Can you apply a lifetime trait bound on ContextDecider
? (So that you get ContextDecider<'a>instead of having the lifetime only on
decide`.)
This would lead to this:
trait Context {
fn give(&self) -> usize;
}
trait ContextDecider<'a> {
fn decide(&'a self, context: &dyn Context) -> &'a str;
}
struct SomeDecider(Vec<String>);
impl<'a> ContextDecider<'a> for SomeDecider {
fn decide(&'a self, context: &dyn Context) -> &'a str {
let some_context = context.give();
if some_context > self.0.len() {
panic!("Oh no!");
}
&self.0[some_context]
}
}
impl<'f, F> ContextDecider<'f> for F
where
F: 'f,
for<'ctx>F: Fn(&'ctx dyn Context) -> &'f str,
{
fn decide(&'f self, giver: &dyn Context) -> &'f str {
self(giver)
}
}
Upvotes: 2