1

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>?

Example

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.

mr.celo
  • 585
  • 1
  • 5
  • 17
  • It looks like your question might be answered by the answers of [How does for<> syntax differ from a regular lifetime bound?](https://stackoverflow.com/q/35592750/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Apr 13 '20 at 17:50
  • @Shepmaster ins't the link a different scenario? For example, binding the lifetime with a HRTB I get [this](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=169ce5156e648ea9f63062c954540250). Or am I missing something? I know that you are really good at keeping the Rust questions at a high level, so I'm trying my best here to apply the answer from the link provided before I say that they are different. But in a way, the question is about expressing and syntax, and the answer does not really help with that. – mr.celo Apr 13 '20 at 18:20
  • If I was 100% sure that the question was answered by the proposed duplicate, I would have closed it myself. It's more of an attempt to provide help in the few minutes I have. – Shepmaster Apr 13 '20 at 18:24
  • 1
    Note that even if *this* code worked, it's still not possible to make a closure return a reference to itself: see [Cannot infer an appropriate lifetime for a closure that returns a reference](https://stackoverflow.com/q/49806352/3650362) for an explanation. I am afraid you may have to find another solution. – trent Apr 15 '20 at 00:01

2 Answers2

2

Can you apply a lifetime trait bound on ContextDecider? (So that you get ContextDecider<'a>instead of having the lifetime only ondecide`.)

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)
    }
}
phimuemue
  • 34,669
  • 9
  • 84
  • 115
  • Indeed this would work. But I was after being able to express it in the closure bounds in the where clause. Changing the `trait` signature may be unfeasible or require too heavy a refactoring. Thanks for the answer, it will help many developers in many other cases and valid to be here. Though, I can't mark it as the answer. – mr.celo Apr 15 '20 at 14:43
1

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(&()));
}
phimuemue
  • 34,669
  • 9
  • 84
  • 115
  • I see what you are doing. Interesting! You take ownership of the closure, and from there on you can refer to `self` lifetime. This is great and begs to experiment with `From` for the wrapper to see if this intermediate step between the closure and the trait can be transparent – mr.celo Apr 18 '20 at 11:33