0

Here's an example that compiles fine:

use std::cell::RefCell;
use std::rc::Rc;

struct Foo<'a> {
    val: Rc<RefCell<i32>>,
    dummy: Option<&'a i32>,
}

fn consume<T>(_: T) {}

impl<'a> Foo<'a> {
    // Note that &i32 has no lifetime markers
    fn subscribe<F>(self, func: F)
    where
        F: Fn(&i32) + 'a,
    {
        let val = self.val.clone();
        consume(move |x: i32| {
            *val.borrow_mut() = x;
            func(&*val.borrow())
        })
    }
}

Here's what what I'm trying to achieve and doesn't compile:

use std::cell::RefCell;
use std::rc::Rc;

trait Stream<'a> {
    type Item: 'a;

    fn subscribe<F>(self, func: F)
    where
        F: Fn(Self::Item) + 'a;
}

struct Bar<'a, S: Stream<'a>> {
    stream: S,
    val: Rc<RefCell<S::Item>>,
}

impl<'a, S: Stream<'a>> Stream<'a> for Bar<'a, S> {
    type Item = &'a S::Item; // 'a doesn't seem right here...

    fn subscribe<F>(self, func: F)
    where
        F: Fn(Self::Item) + 'a,
    {
        let val = self.val.clone();
        self.stream.subscribe(move |x: S::Item| {
            *val.borrow_mut() = x;
            func(&*val.borrow());
        })
    }
}

This example is almost identical to the first. The only difference is that because it's a trait, we have to assign an explicit lifetime to the associated type, Item, which is a reference. Setting it to 'a causes lifetime conflicts (rightfully so):

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/lib.rs:27:24
   |
27 |             func(&*val.borrow());
   |                        ^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime  as defined on the body at 25:31...
  --> src/lib.rs:25:31
   |
25 |         self.stream.subscribe(move |x: S::Item| {
   |                               ^^^^^^^^^^^^^^^^^
note: ...so that closure can access `val`
  --> src/lib.rs:27:20
   |
27 |             func(&*val.borrow());
   |                    ^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 17:6...
  --> src/lib.rs:17:6
   |
17 | impl<'a, S: Stream<'a>> Stream<'a> for Bar<'a, S> {
   |      ^^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:27:18
   |
27 |             func(&*val.borrow());
   |                  ^^^^^^^^^^^^^^

In fact, the first example can be modified to fail with exactly the same error if you were to replace Fn(&i32) with Fn(&'a i32) in the function signature.

Is it possible to make the second example compile? Possibly via the use of some hacks or unsafe blocks, I'd be willing to accept anything really. Changing signatures or reshuffling the logic if that's required. What should the lifetime of the associated type Item be?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
aldanor
  • 3,371
  • 2
  • 26
  • 26
  • I believe your question is answered by the answers of [How do I write an iterator that returns references to itself?](https://stackoverflow.com/q/30422177/155423). If you disagree, please [edit] your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Oct 28 '18 at 21:07
  • @Shepmaster Thanks -- as a matter of fact I've read that question just earlier today. Please correct me if I'm wrong, but this one's a bit different since, first, `self` in the trait method is by-value and not a mutable reference like in the iterator case? And, second, the reference doesn't escape the trait method, it's only passed to a closure that this method takes? This led me to think that there's ways to solve it (unlike the streaming-iterator case which seems to impose more restrictions) – aldanor Oct 28 '18 at 21:18

1 Answers1

0

You can make the first example compile by specifying that F must be able to deal with more generic lifetimes:

impl<'a> Foo<'a> {
    fn subscribe<F>(self, func: F)
    where
        for<'b> F: Fn(&'b i32) + 'a, // f can cope with any lifetime 'b
    {
        let val = self.val.clone();
        consume(move |x| {
            *val.borrow_mut() = x;
            func(&*val.borrow())
        })
    }
}

As far as I can see, your second example suffers from at least one other problem: You call

self.stream.subscribe(move |x: S::Item| {
    *val.borrow_mut() = x;
    func(&*val.borrow());
})

but subscribe accepts a function accepting a borrow (i.e. &S::Item, not S::Item). If you pass in a reference, I am not sure if/how you can assign that to val.borrow_mut. You possibly would have to convert it to an owned value.

As you already mention, you may also set Item = S::Item (without a borrow). This, however means that you cannot simply pass val.borrow() to func within the closure, since that would move from a borrowed value. Again, the solution could be to somehow convert it to an owned value.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
phimuemue
  • 34,669
  • 9
  • 84
  • 115