Transferring ownership of trait objects

Hey,
I am currently working on a library (rxRust to be precise) where I have to do insertion into graph-like structures, and am now running into the following problem:

To append a node to the graph, the following function is defined by the library:

fn actual_subscribe<O>(self, observer: O) -> Self::Unsub
where
  O: Observer<Item = Self::Item, Err = Self::Err> + 'a;

I am now implementing this function for an operator with the following signature:

pub struct GroupObservable<Discr, Key, Item, Err> {
  source: Rc<RefCell<GroupObserver2<Discr, Key, Item, Err>>>,
}

Due to the nature of the problem I am solving, I need to have multiple writers to the GroupObserver2, which is implemented by the Rc<RefCell<>> construct.

The GroupObservable appends the following object to the graph:

struct GroupObserver<Item, Err> {
  observer: Box<dyn Observer<Err = Err, Item = Item>>,
}

Since I only know the exact type of the following node at runtime, and the parent node is constructed before the child node, the only solution I could come up with was boxing the node as a trait object.

To wrap it up, the implementation for GroupObservable to append GroupObserver to the graph is the following:

fn actual_subscribe<O>(self, observer: O) -> Self::Unsub
where
  O: Observer<Item = Self::Item, Err = Self::Err> + 'a,
{
    (*(self.source))
      .borrow_mut()
      .actual_subscribe(GroupObserver {
        observer: Box::new(observer),
        discr: self.discr,
        key: self.key,
      });

  SingleSubscription::default()
}

When compiling this code, I receive the following error:

error[E0310]: the parameter type `O` may not live long enough
   --> src/ops/group_by.rs:132:21
    |
132 |           observer: Box::new(observer),
    |                     ^^^^^^^^^^^^^^^^^^ ...so that the type `O` will meet its required lifetime bounds
    |
help: consider adding an explicit lifetime bound...
    |
127 |     O: Observer<Item = Self::Item, Err = Self::Err> + 'a + 'static,
    |                                                          +++++++++

For more information about this error, try `rustc --explain E0310`.

The proposed solution is not viable, as the constraints are given by the library.

I think I understand where the issue is coming from (observer does not implement Clone, so the value stays wherever it is now, and Box just creates a pointer to it, resulting in not being able to control its lifetime), but is there really no option to move the ownership of a trait object to a struct in Rust?

Title and last paragraph seems off. Likely the error help is wrong choice of advice.

This is what you have;

struct GroupObserver<Item, Err> {
  observer: Box<dyn Observer<Err = Err, Item = Item> + 'static>,
}

The 'static is the default, just implied. So you need to add a different lifetime here.

But why doesn't GroupObserver.observer just take ownership of the content?

It does, that's not the problem. The problem is that the trait object itself might have been constructed from a value that has an embedded borrow, hence an associated non-'static lifetime. You have to be explicit about this lifetime if you want to change the implied default. (A value having a lifetime bound 'a doesn't satisfy 'static even if you hold it by-value, because that's not what the lifetime annotation means in this context.)

fn actual_subscribe<O>(self, observer: O)

O has a lifetime of the callers choosing. When the lifetimes don't match up you have different types and the compiler will block such action to stop memory unsafe usage.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.